From ae0b374faad06b74d053257a0370e9e24b59569b Mon Sep 17 00:00:00 2001 From: Karol Kaczmarek Date: Tue, 28 Apr 2015 15:05:39 -0700 Subject: [PATCH 1/8] Adding DscExamplesPresent rule scaffolding --- Rules/DscExamplesPresent.cs | 105 ++++++++++++++++++++++++++++++++++++ Rules/Strings.Designer.cs | 27 ++++++++++ Rules/Strings.resx | 9 ++++ 3 files changed, 141 insertions(+) create mode 100644 Rules/DscExamplesPresent.cs diff --git a/Rules/DscExamplesPresent.cs b/Rules/DscExamplesPresent.cs new file mode 100644 index 000000000..573a8bc82 --- /dev/null +++ b/Rules/DscExamplesPresent.cs @@ -0,0 +1,105 @@ +// +// Copyright (c) Microsoft Corporation. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation.Language; +using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using System.ComponentModel.Composition; +using System.Globalization; + +namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +{ + /// + /// DscExamplesExist: Checks that DSC examples for given resource are present + /// + [Export(typeof(IDSCResourceRule))] + public class DscExamplesPresent : IDSCResourceRule + { + /// + /// AnalyzeDSCResource: Analyzes given DSC Resource + /// + /// The script's ast + /// The name of the script file being analyzed + /// The results of the analysis + public IEnumerable AnalyzeDSCResource(Ast ast, string fileName) + { + + } + + /// + /// AnalyzeDSCClass: Analyzes given DSC class + /// + /// + /// + /// + public IEnumerable AnalyzeDSCClass(Ast ast, string fileName) + { + + } + + + /// + /// GetName: Retrieves the name of this rule. + /// + /// The name of this rule + public string GetName() + { + return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.DscExamplesPresent); + } + + /// + /// GetCommonName: Retrieves the Common name of this rule. + /// + /// The common name of this rule + public string GetCommonName() + { + return string.Format(CultureInfo.CurrentCulture, Strings.DscExamplesPresentCommonName); + } + + /// + /// GetDescription: Retrieves the description of this rule. + /// + /// The description of this rule + public string GetDescription() + { + return string.Format(CultureInfo.CurrentCulture, Strings.DscExamplesPresentDescription); + } + + /// + /// GetSourceType: Retrieves the type of the rule: builtin, managed or module. + /// + public SourceType GetSourceType() + { + return SourceType.Builtin; + } + + /// + /// GetSeverity: Retrieves the severity of the rule: error, warning or information. + /// + /// + public RuleSeverity GetSeverity() + { + return RuleSeverity.Information; + } + + /// + /// GetSourceName: Retrieves the module/assembly name the rule is from. + /// + public string GetSourceName() + { + return string.Format(CultureInfo.CurrentCulture, Strings.DSCSourceName); + } + } + +} \ No newline at end of file diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index 3f1eb4469..d8bb1ac40 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -825,6 +825,33 @@ internal static string CommandNotFoundName { } } + /// + /// Looks up a localized string similar to DscExamplesPresent. + /// + internal static string DscExamplesPresent { + get { + return ResourceManager.GetString("DscExamplesPresent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DSC examples are present. + /// + internal static string DscExamplesPresentCommonName { + get { + return ResourceManager.GetString("DscExamplesPresentCommonName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every DSC resource module should contain folder "Examples" with sample configurations for every resource. Sample configurations should have resource name they are demonstrating in the title.. + /// + internal static string DscExamplesPresentDescription { + get { + return ResourceManager.GetString("DscExamplesPresentDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to PSDSC. /// diff --git a/Rules/Strings.resx b/Rules/Strings.resx index 1d548b83a..f06b0a2da 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -690,4 +690,13 @@ UseOutputTypeCorrectly + + DscExamplesPresent + + + DSC examples are present + + + Every DSC resource module should contain folder "Examples" with sample configurations for every resource. Sample configurations should have resource name they are demonstrating in the title. + \ No newline at end of file From 6ff3193367f8bca4c1b3b1993dc8670968e37cb2 Mon Sep 17 00:00:00 2001 From: Karol Kaczmarek Date: Tue, 28 Apr 2015 15:07:35 -0700 Subject: [PATCH 2/8] Fix typo --- Rules/ReturnCorrectTypesForDSCFunctions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules/ReturnCorrectTypesForDSCFunctions.cs b/Rules/ReturnCorrectTypesForDSCFunctions.cs index 2890660fb..90aeeeabc 100644 --- a/Rules/ReturnCorrectTypesForDSCFunctions.cs +++ b/Rules/ReturnCorrectTypesForDSCFunctions.cs @@ -210,7 +210,7 @@ public SourceType GetSourceType() } /// - /// GetSeverity: Retrieves the severity of the rule: error, warning of information. + /// GetSeverity: Retrieves the severity of the rule: error, warning or information. /// /// public RuleSeverity GetSeverity() From d94733cc7da41ae1007287e99dde170f5fb10194 Mon Sep 17 00:00:00 2001 From: Karol Kaczmarek Date: Mon, 4 May 2015 17:16:00 -0700 Subject: [PATCH 3/8] Adding implementation for class based resources --- Rules/DscExamplesPresent.cs | 67 ++++++++++++++++++++++++- Rules/ScriptAnalyzerBuiltinRules.csproj | 1 + Rules/Strings.Designer.cs | 9 ++++ Rules/Strings.resx | 3 ++ 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/Rules/DscExamplesPresent.cs b/Rules/DscExamplesPresent.cs index 573a8bc82..84f6d5268 100644 --- a/Rules/DscExamplesPresent.cs +++ b/Rules/DscExamplesPresent.cs @@ -17,11 +17,17 @@ using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; +using System.IO; +using System.Management.Automation; namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules { /// - /// DscExamplesExist: Checks that DSC examples for given resource are present + /// DscExamplesPresent: Checks that DSC examples for given resource are present. + /// Rule expects directory Examples to be present: + /// For non-class based resources it should exist at the same folder level as DSCResources folder. + /// For class based resources it should be present at the same folder level as resource psm1 file. + /// Examples folder should contain sample configuration for given resource - file name should contain resource's name. /// [Export(typeof(IDSCResourceRule))] public class DscExamplesPresent : IDSCResourceRule @@ -34,7 +40,29 @@ public class DscExamplesPresent : IDSCResourceRule /// The results of the analysis public IEnumerable AnalyzeDSCResource(Ast ast, string fileName) { + String fileNameOnly = fileName.Substring(fileName.LastIndexOf("\\", StringComparison.Ordinal) + 1); + String resourceName = fileNameOnly.Substring(0, fileNameOnly.Length - ".psm1".Length); + String examplesQuery = "*" + resourceName + "*"; + Boolean examplesPresent = false; + String expectedExamplesPath = fileName + "\\..\\..\\..\\Examples"; + // Verify examples are present + if (Directory.Exists(expectedExamplesPath)) + { + DirectoryInfo examplesFolder = new DirectoryInfo(expectedExamplesPath); + FileInfo[] exampleFiles = examplesFolder.GetFiles(examplesQuery); + if (exampleFiles.Length != 0) + { + examplesPresent = true; + } + } + + // Return error if no examples present + if (!examplesPresent) + { + yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.DscExamplesPresentNoExamplesError, resourceName), + null, GetName(), DiagnosticSeverity.Information, fileName); + } } /// @@ -45,9 +73,44 @@ public IEnumerable AnalyzeDSCResource(Ast ast, string fileName /// public IEnumerable AnalyzeDSCClass(Ast ast, string fileName) { + String resourceName = null; - } + // Obtain class based resource name + IEnumerable typeDefinitionAsts = (ast.FindAll(dscAst => dscAst is TypeDefinitionAst, true)); + foreach (TypeDefinitionAst typeDefinitionAst in typeDefinitionAsts) + { + var attributes = typeDefinitionAst.Attributes; + foreach(var attribute in attributes) + { + if (attribute.TypeName.FullName.Equals("DscResource")) + { + resourceName = typeDefinitionAst.Name; + } + } + } + + String examplesQuery = "*" + resourceName + "*"; + Boolean examplesPresent = false; + String expectedExamplesPath = fileName + "\\..\\Examples"; + // Verify examples are present + if (Directory.Exists(expectedExamplesPath)) + { + DirectoryInfo examplesFolder = new DirectoryInfo(expectedExamplesPath); + FileInfo[] exampleFiles = examplesFolder.GetFiles(examplesQuery); + if (exampleFiles.Length != 0) + { + examplesPresent = true; + } + } + + // Return error if no examples present + if (!examplesPresent) + { + yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.DscExamplesPresentNoExamplesError, resourceName), + null, GetName(), DiagnosticSeverity.Information, fileName); + } + } /// /// GetName: Retrieves the name of this rule. diff --git a/Rules/ScriptAnalyzerBuiltinRules.csproj b/Rules/ScriptAnalyzerBuiltinRules.csproj index eef54841b..a11c83100 100644 --- a/Rules/ScriptAnalyzerBuiltinRules.csproj +++ b/Rules/ScriptAnalyzerBuiltinRules.csproj @@ -68,6 +68,7 @@ + diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index d8bb1ac40..ef44c1fcd 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -852,6 +852,15 @@ internal static string DscExamplesPresentDescription { } } + /// + /// Looks up a localized string similar to No examples found for resource '{0}'. + /// + internal static string DscExamplesPresentNoExamplesError { + get { + return ResourceManager.GetString("DscExamplesPresentNoExamplesError", resourceCulture); + } + } + /// /// Looks up a localized string similar to PSDSC. /// diff --git a/Rules/Strings.resx b/Rules/Strings.resx index f06b0a2da..c57821d3e 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -699,4 +699,7 @@ Every DSC resource module should contain folder "Examples" with sample configurations for every resource. Sample configurations should have resource name they are demonstrating in the title. + + No examples found for resource '{0}' + \ No newline at end of file From 823ac98ef3a282a7a55f79cfb7911bf3efcaaa26 Mon Sep 17 00:00:00 2001 From: Karol Kaczmarek Date: Mon, 4 May 2015 17:42:54 -0700 Subject: [PATCH 4/8] Adding tests for class based resource --- Tests/Rules/DscExamplesPresent.tests.ps1 | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Tests/Rules/DscExamplesPresent.tests.ps1 diff --git a/Tests/Rules/DscExamplesPresent.tests.ps1 b/Tests/Rules/DscExamplesPresent.tests.ps1 new file mode 100644 index 000000000..763d0281b --- /dev/null +++ b/Tests/Rules/DscExamplesPresent.tests.ps1 @@ -0,0 +1,37 @@ +Import-Module -Verbose PSScriptAnalyzer + +$currentPath = Split-Path -Parent $MyInvocation.MyCommand.Path +$ruleName = "PSDSCDscExamplesPresent" + +Describe "DscExamplesPresent rule in class based resource" { + + $examplesPath = "$currentPath\DSCResources\MyDscResource\Examples" + $classResourcePath = "$currentPath\DSCResources\MyDscResource\MyDscResource.psm1" + + Context "When examples absent" { + + $violations = Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue $classResourcePath | Where-Object {$_.RuleName -eq $ruleName} + $violationMessage = "No examples found for resource 'FileResource'" + + It "has 1 missing examples violation" { + $violations.Count | Should Be 1 + } + + It "has the correct description message" { + $violations[0].Message | Should Match $violationMessage + } + } + + Context "When examples present" { + New-Item -Path $examplesPath -ItemType Directory + New-Item -Path "$examplesPath\FileResource_Example.psm1" -ItemType File + + $noViolations = Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue $classResourcePath | Where-Object {$_.RuleName -eq $ruleName} + + It "returns no violations" { + $noViolations.Count | Should Be 0 + } + + Remove-Item -Path $examplesPath -Recurse -Force + } +} \ No newline at end of file From da42209ff9d403e2cca0369e1c4740e328a4c2ef Mon Sep 17 00:00:00 2001 From: Karol Kaczmarek Date: Tue, 5 May 2015 14:18:03 -0700 Subject: [PATCH 5/8] Changes in AnalyzeDSCClass to be called only for class based resource --- Rules/DscExamplesPresent.cs | 50 +++++++++++------------- Tests/Rules/DscExamplesPresent.tests.ps1 | 33 ++++++++++++++++ 2 files changed, 56 insertions(+), 27 deletions(-) diff --git a/Rules/DscExamplesPresent.cs b/Rules/DscExamplesPresent.cs index 84f6d5268..a946d0622 100644 --- a/Rules/DscExamplesPresent.cs +++ b/Rules/DscExamplesPresent.cs @@ -75,41 +75,37 @@ public IEnumerable AnalyzeDSCClass(Ast ast, string fileName) { String resourceName = null; - // Obtain class based resource name - IEnumerable typeDefinitionAsts = (ast.FindAll(dscAst => dscAst is TypeDefinitionAst, true)); - foreach (TypeDefinitionAst typeDefinitionAst in typeDefinitionAsts) + IEnumerable dscClasses = ast.FindAll(item => + item is TypeDefinitionAst + && ((item as TypeDefinitionAst).IsClass) + && (item as TypeDefinitionAst).Attributes.Any(attr => String.Equals("DSCResource", attr.TypeName.FullName, StringComparison.OrdinalIgnoreCase)), true); + + foreach (TypeDefinitionAst dscClass in dscClasses) { - var attributes = typeDefinitionAst.Attributes; - foreach(var attribute in attributes) + resourceName = dscClass.Name; + + String examplesQuery = "*" + resourceName + "*"; + Boolean examplesPresent = false; + String expectedExamplesPath = fileName + "\\..\\Examples"; + + // Verify examples are present + if (Directory.Exists(expectedExamplesPath)) { - if (attribute.TypeName.FullName.Equals("DscResource")) + DirectoryInfo examplesFolder = new DirectoryInfo(expectedExamplesPath); + FileInfo[] exampleFiles = examplesFolder.GetFiles(examplesQuery); + if (exampleFiles.Length != 0) { - resourceName = typeDefinitionAst.Name; + examplesPresent = true; } } - } - - String examplesQuery = "*" + resourceName + "*"; - Boolean examplesPresent = false; - String expectedExamplesPath = fileName + "\\..\\Examples"; - // Verify examples are present - if (Directory.Exists(expectedExamplesPath)) - { - DirectoryInfo examplesFolder = new DirectoryInfo(expectedExamplesPath); - FileInfo[] exampleFiles = examplesFolder.GetFiles(examplesQuery); - if (exampleFiles.Length != 0) + // Return error if no examples present + if (!examplesPresent) { - examplesPresent = true; + yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.DscExamplesPresentNoExamplesError, resourceName), + null, GetName(), DiagnosticSeverity.Information, fileName); } - } - - // Return error if no examples present - if (!examplesPresent) - { - yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.DscExamplesPresentNoExamplesError, resourceName), - null, GetName(), DiagnosticSeverity.Information, fileName); - } + } } /// diff --git a/Tests/Rules/DscExamplesPresent.tests.ps1 b/Tests/Rules/DscExamplesPresent.tests.ps1 index 763d0281b..e27459a9a 100644 --- a/Tests/Rules/DscExamplesPresent.tests.ps1 +++ b/Tests/Rules/DscExamplesPresent.tests.ps1 @@ -32,6 +32,39 @@ Describe "DscExamplesPresent rule in class based resource" { $noViolations.Count | Should Be 0 } + Remove-Item -Path $examplesPath -Recurse -Force + } +} + +Describe "DscExamplesPresent rule in regular (non-class) based resource" { + + $examplesPath = "$currentPath\Examples" + $resourcePath = "$currentPath\DSCResources\MSFT_WaitForAll\MSFT_WaitForAll.psm1" + + Context "When examples absent" { + + $violations = Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue $resourcePath | Where-Object {$_.RuleName -eq $ruleName} + $violationMessage = "No examples found for resource 'MSFT_WaitForAll'" + + It "has 1 missing examples violation" { + $violations.Count | Should Be 1 + } + + It "has the correct description message" { + $violations[0].Message | Should Match $violationMessage + } + } + + Context "When examples present" { + New-Item -Path $examplesPath -ItemType Directory + New-Item -Path "$examplesPath\MSFT_WaitForAll_Example.psm1" -ItemType File + + $noViolations = Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue $resourcePath | Where-Object {$_.RuleName -eq $ruleName} + + It "returns no violations" { + $noViolations.Count | Should Be 0 + } + Remove-Item -Path $examplesPath -Recurse -Force } } \ No newline at end of file From 110b53c92a4530ef2e8dcbc41bf09af66a6c61ac Mon Sep 17 00:00:00 2001 From: Karol Kaczmarek Date: Tue, 5 May 2015 15:06:01 -0700 Subject: [PATCH 6/8] Replacing string operations with functions --- Rules/DscExamplesPresent.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Rules/DscExamplesPresent.cs b/Rules/DscExamplesPresent.cs index a946d0622..1a90379f3 100644 --- a/Rules/DscExamplesPresent.cs +++ b/Rules/DscExamplesPresent.cs @@ -40,11 +40,11 @@ public class DscExamplesPresent : IDSCResourceRule /// The results of the analysis public IEnumerable AnalyzeDSCResource(Ast ast, string fileName) { - String fileNameOnly = fileName.Substring(fileName.LastIndexOf("\\", StringComparison.Ordinal) + 1); - String resourceName = fileNameOnly.Substring(0, fileNameOnly.Length - ".psm1".Length); - String examplesQuery = "*" + resourceName + "*"; + String fileNameOnly = Path.GetFileName(fileName); + String resourceName = Path.GetFileNameWithoutExtension(fileNameOnly); + String examplesQuery = String.Format("*{0}*", resourceName); Boolean examplesPresent = false; - String expectedExamplesPath = fileName + "\\..\\..\\..\\Examples"; + String expectedExamplesPath = Path.Combine(new String[] {fileName, "..", "..", "..", "Examples"}); // Verify examples are present if (Directory.Exists(expectedExamplesPath)) @@ -84,9 +84,9 @@ item is TypeDefinitionAst { resourceName = dscClass.Name; - String examplesQuery = "*" + resourceName + "*"; + String examplesQuery = String.Format("*{0}*", resourceName); Boolean examplesPresent = false; - String expectedExamplesPath = fileName + "\\..\\Examples"; + String expectedExamplesPath = Path.Combine(new String[] {fileName, "..", "Examples"}); // Verify examples are present if (Directory.Exists(expectedExamplesPath)) From 96342d95eb91c0bedd7828d410a3e77a6aa6c56d Mon Sep 17 00:00:00 2001 From: Karol Kaczmarek Date: Tue, 5 May 2015 15:35:23 -0700 Subject: [PATCH 7/8] Populating extent --- Rules/DscExamplesPresent.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rules/DscExamplesPresent.cs b/Rules/DscExamplesPresent.cs index 1a90379f3..4d3129c4c 100644 --- a/Rules/DscExamplesPresent.cs +++ b/Rules/DscExamplesPresent.cs @@ -61,7 +61,7 @@ public IEnumerable AnalyzeDSCResource(Ast ast, string fileName if (!examplesPresent) { yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.DscExamplesPresentNoExamplesError, resourceName), - null, GetName(), DiagnosticSeverity.Information, fileName); + ast.Extent, GetName(), DiagnosticSeverity.Information, fileName); } } @@ -103,7 +103,7 @@ item is TypeDefinitionAst if (!examplesPresent) { yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.DscExamplesPresentNoExamplesError, resourceName), - null, GetName(), DiagnosticSeverity.Information, fileName); + dscClass.Extent, GetName(), DiagnosticSeverity.Information, fileName); } } } From 8fd01d17d2b979055b73ab2380114edf57851f24 Mon Sep 17 00:00:00 2001 From: KarolKaczmarek Date: Tue, 5 May 2015 17:05:30 -0700 Subject: [PATCH 8/8] Create documentation in DscExamplesPresent.md --- RuleDocumentation/DscExamplesPresent.md | 55 +++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 RuleDocumentation/DscExamplesPresent.md diff --git a/RuleDocumentation/DscExamplesPresent.md b/RuleDocumentation/DscExamplesPresent.md new file mode 100644 index 000000000..ab2743642 --- /dev/null +++ b/RuleDocumentation/DscExamplesPresent.md @@ -0,0 +1,55 @@ +#DscExamplesPresent +**Severity Level: Information** + + +##Description + +Checks that DSC examples for given resource are present. + +##How to Fix + +To fix a violation of this rule, please make sure Examples directory is present: +* For non-class based resources it should exist at the same folder level as DSCResources folder. +* For class based resources it should be present at the same folder level as resource psm1 file. + +Examples folder should contain sample configuration for given resource - file name should contain resource's name. + +##Example + +### Non-class based resource + +Let's assume we have non-class based resource with a following file structure: + +* xAzure + * DSCResources + * MSFT_xAzureSubscription + * MSFT_xAzureSubscription.psm1 + * MSFT_xAzureSubscription.schema.mof + +In this case, to fix this warning, we should add examples in a following way: + +* xAzure + * DSCResources + * MSFT_xAzureSubscription + * MSFT_xAzureSubscription.psm1 + * MSFT_xAzureSubscription.schema.mof + * Examples + * MSFT_xAzureSubscription_AddSubscriptionExample.ps1 + * MSFT_xAzureSubscription_RemoveSubscriptionExample.ps1 + +### Class based resource + +Let's assume we have class based resource with a following file structure: + +* MyDscResource + * MyDscResource.psm1 + * MyDscresource.psd1 + +In this case, to fix this warning, we should add examples in a following way: + +* MyDscResource + * MyDscResource.psm1 + * MyDscresource.psd1 + * Tests + * MyDscResource_Example1.ps1 + * MyDscResource_Example2.ps1