diff --git a/DSCResources/MSFT_xComputer/MSFT_xComputer.psm1 b/DSCResources/MSFT_xComputer/MSFT_xComputer.psm1 index 92056ffd..5f31cb0d 100644 --- a/DSCResources/MSFT_xComputer/MSFT_xComputer.psm1 +++ b/DSCResources/MSFT_xComputer/MSFT_xComputer.psm1 @@ -9,10 +9,14 @@ function Get-TargetResource param ( [parameter(Mandatory)] + [ValidateLength(1,15)] + [ValidateScript({$_ -inotmatch'[\/\\:*?"<>|]' })] [string] $Name, [string] $DomainName, + [string] $JoinOU, + [PSCredential] $Credential, [PSCredential] $UnjoinCredential, @@ -26,6 +30,8 @@ function Get-TargetResource $returnValue = @{ Name = $env:COMPUTERNAME DomainName = GetComputerDomain + JoinOU = $JoinOU + CurrentOU = Get-ComputerOU Credential = [ciminstance]$convertToCimCredential UnjoinCredential = [ciminstance]$convertToCimUnjoinCredential WorkGroupName= (gwmi WIN32_ComputerSystem).WorkGroup @@ -39,9 +45,13 @@ function Set-TargetResource param ( [parameter(Mandatory)] + [ValidateLength(1,15)] + [ValidateScript({$_ -inotmatch'[\/\\:*?"<>|]' })] [string] $Name, [string] $DomainName, + + [string] $JoinOU, [PSCredential] $Credential, @@ -73,7 +83,12 @@ function Set-TargetResource } else { - Add-Computer -DomainName $DomainName -Credential $Credential -NewName $Name -Force + if ($JoinOU) { + Add-Computer -DomainName $DomainName -Credential $Credential -NewName $Name -OUPath $JoinOU -Force + } + else { + Add-Computer -DomainName $DomainName -Credential $Credential -NewName $Name -Force + } } Write-Verbose -Message "Renamed computer to '$($Name)' and added to the domain '$($DomainName)." } @@ -86,7 +101,12 @@ function Set-TargetResource } else { - Add-Computer -DomainName $DomainName -Credential $Credential -Force + if ($JoinOU) { + Add-Computer -DomainName $DomainName -Credential $Credential -OUPath $JoinOU -Force + } + else { + Add-Computer -DomainName $DomainName -Credential $Credential -Force + } } Write-Verbose -Message "Added computer to domain '$($DomainName)." } @@ -181,7 +201,11 @@ function Test-TargetResource param ( [parameter(Mandatory)] + [ValidateLength(1,15)] + [ValidateScript({$_ -inotmatch'[\/\\:*?"<>|]' })] [string] $Name, + + [string] $JoinOU, [PSCredential]$Credential, @@ -191,6 +215,8 @@ function Test-TargetResource [string] $WorkGroupName ) + + Write-Verbose -Message "Validate desired Name is a valid name" Write-Verbose -Message "Checking if computer name is $Name" if ($Name -ne $env:COMPUTERNAME) {return $false} @@ -247,5 +273,18 @@ function GetComputerDomain } } -Export-ModuleMember -Function *-TargetResource +function Get-ComputerOU +{ + $ou = $null + if (GetComputerDomain) + { + $dn = $null + $dn = ([adsisearcher]"(&(objectCategory=computer)(objectClass=computer)(cn=$env:COMPUTERNAME))").FindOne().Properties.distinguishedname + $ou = $dn -replace '^(CN=.*?(?<=,))', '' + } + + return $ou +} + +Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_xComputer/MSFT_xComputer.schema.mof b/DSCResources/MSFT_xComputer/MSFT_xComputer.schema.mof index ef592e05..44bc6b97 100644 --- a/DSCResources/MSFT_xComputer/MSFT_xComputer.schema.mof +++ b/DSCResources/MSFT_xComputer/MSFT_xComputer.schema.mof @@ -1,9 +1,11 @@ -[ClassVersion("1.0.1.0"), FriendlyName("xComputer")] -class MSFT_xComputer : OMI_BaseResource -{ - [key] string Name; - [write] string DomainName; - [write,EmbeddedInstance("MSFT_Credential")] String Credential; - [write,EmbeddedInstance("MSFT_Credential")] String UnjoinCredential; - [write] string WorkGroupName; -}; +[ClassVersion("1.0.1.0"), FriendlyName("xComputer")] +class MSFT_xComputer : OMI_BaseResource +{ + [key] string Name; + [write] string DomainName; + [write] string JoinOU; + [read] string CurrentOU; + [write,EmbeddedInstance("MSFT_Credential")] String Credential; + [write,EmbeddedInstance("MSFT_Credential")] String UnjoinCredential; + [write] string WorkGroupName; +}; diff --git a/README.md b/README.md index fb1beb55..abb8b8e1 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,17 @@ xComputer resource has following properties: * Name: The desired computer name * DomainName: The name of the domain to join +* JoinOU: The distinguished name of the organizational unit that the computer account will be created in * WorkGroupName: The name of the workgroup * Credential: Credential to be used to join or leave domain +* CurrentOU: A read-only property that specifies the organizational unit that the computer account is currently in ## Versions +### Unreleased + +### 1.4.0.0 +* Adding Name parameter validation ### 1.3.0 diff --git a/Tests/xComputermanagement.Tests.ps1 b/Tests/xComputermanagement.Tests.ps1 index 5166519e..5d94a14a 100644 --- a/Tests/xComputermanagement.Tests.ps1 +++ b/Tests/xComputermanagement.Tests.ps1 @@ -82,16 +82,28 @@ InModuleScope MSFT_xComputer { Mock GetComputerDomain {'contoso.com'} Test-TargetResource -Name $Env:ComputerName -WorkGroupName 'Contoso' -Credential $Credential -UnjoinCredential $Credential | Should Be $false } + It 'Throws if name is to long' { + {Test-TargetResource -Name "ThisNameIsTooLong"} | Should Throw + } + It 'Throws if name contains illigal characters' { + {Test-TargetResource -Name "ThisIsBad<>"} | Should Throw + } } Context Get-TargetResource { It 'should not throw' { {Get-TargetResource -Name $env:COMPUTERNAME} | Should Not Throw } - It 'Should return a hashtable containing Name,DomainName, Credential, UnjoinCredential and WorkGroupName' { + It 'Should return a hashtable containing Name, DomainName, JoinOU, CurrentOU, Credential, UnjoinCredential and WorkGroupName' { $Result = Get-TargetResource -Name $env:COMPUTERNAME $Result.GetType().Fullname | Should Be 'System.Collections.Hashtable' - $Result.Keys | Should Be @('Name','DomainName','Credential','UnjoinCredential','WorkGroupName') + $Result.Keys | Should Be @('Name', 'DomainName', 'JoinOU', 'CurrentOU', 'Credential', 'UnjoinCredential', 'WorkGroupName') + } + It 'Throws if name is to long' { + {Get-TargetResource -Name "ThisNameIsTooLong"} | Should Throw + } + It 'Throws if name contains illigal characters' { + {Get-TargetResource -Name "ThisIsBad<>"} | Should Throw } } Context Set-TargetResource { @@ -115,6 +127,14 @@ InModuleScope MSFT_xComputer { Assert-MockCalled -CommandName Add-Computer -Exactly 1 -Scope It -ParameterFilter {$DomainName -and $NewName} Assert-MockCalled -CommandName Add-Computer -Exactly 0 -Scope It -ParameterFilter {$WorkGroupName} } + It 'Changes ComputerName and changes Domain to new Domain with specified OU' { + Mock Get-WMIObject {[PSCustomObject]@{Domain = 'Contoso.com';Workgroup='Contoso.com';PartOfDomain=$true}} + Mock GetComputerDomain {'contoso.com'} + Set-TargetResource -Name $NotComputerName -DomainName 'adventure-works.com' -JoinOU 'OU=Computers,DC=contoso,DC=com' -Credential $Credential -UnjoinCredential $Credential | Should BeNullOrEmpty + Assert-MockCalled -CommandName Rename-Computer -Exactly 0 -Scope It + Assert-MockCalled -CommandName Add-Computer -Exactly 1 -Scope It -ParameterFilter {$DomainName -and $NewName} + Assert-MockCalled -CommandName Add-Computer -Exactly 0 -Scope It -ParameterFilter {$WorkGroupName} + } It 'Changes ComputerName and changes Domain to Workgroup' { Mock Get-WMIObject {[PSCustomObject]@{Domain = 'Contoso.com';Workgroup='Contoso.com';PartOfDomain=$true}} Mock GetComputerDomain {'contoso.com'} @@ -131,6 +151,14 @@ InModuleScope MSFT_xComputer { Assert-MockCalled -CommandName Add-Computer -Exactly 1 -Scope It -ParameterFilter {$DomainName -and $NewName} Assert-MockCalled -CommandName Add-Computer -Exactly 0 -Scope It -ParameterFilter {$WorkGroupName} } + It 'Changes ComputerName and changes Workgroup to Domain with specified OU' { + Mock Get-WMIObject {[PSCustomObject]@{Domain = 'Contoso';Workgroup='Contoso';PartOfDomain=$false}} + Mock GetComputerDomain {''} + Set-TargetResource -Name $NotComputerName -DomainName 'Contoso.com' -JoinOU 'OU=Computers,DC=contoso,DC=com' -Credential $Credential | Should BeNullOrEmpty + Assert-MockCalled -CommandName Rename-Computer -Exactly 0 -Scope It + Assert-MockCalled -CommandName Add-Computer -Exactly 1 -Scope It -ParameterFilter {$DomainName -and $NewName} + Assert-MockCalled -CommandName Add-Computer -Exactly 0 -Scope It -ParameterFilter {$WorkGroupName} + } It 'Changes ComputerName and changes Workgroup to new Workgroup' { Mock Get-WMIObject {[PSCustomObject]@{Domain = 'Contoso';Workgroup='Contoso';PartOfDomain=$false}} Mock GetComputerDomain {''} @@ -148,6 +176,15 @@ InModuleScope MSFT_xComputer { Assert-MockCalled -CommandName Add-Computer -Exactly 0 -Scope It -ParameterFilter {$NewName} Assert-MockCalled -CommandName Add-Computer -Exactly 0 -Scope It -ParameterFilter {$WorkGroupName} } + It 'Changes only the Domain to new Domain with specified OU' { + Mock Get-WMIObject {[PSCustomObject]@{Domain = 'Contoso.com';Workgroup='Contoso.com';PartOfDomain=$true}} + Mock GetComputerDomain {'contoso.com'} + Set-TargetResource -Name $Env:ComputerName -DomainName 'adventure-works.com' -JoinOU 'OU=Computers,DC=contoso,DC=com' -Credential $Credential -UnjoinCredential $Credential | Should BeNullOrEmpty + Assert-MockCalled -CommandName Rename-Computer -Exactly 0 -Scope It + Assert-MockCalled -CommandName Add-Computer -Exactly 1 -Scope It -ParameterFilter {$DomainName} + Assert-MockCalled -CommandName Add-Computer -Exactly 0 -Scope It -ParameterFilter {$NewName} + Assert-MockCalled -CommandName Add-Computer -Exactly 0 -Scope It -ParameterFilter {$WorkGroupName} + } It 'Changes only Domain to Workgroup' { Mock Get-WMIObject {[PSCustomObject]@{Domain = 'Contoso.com';Workgroup='Contoso.com';PartOfDomain=$true}} Mock GetComputerDomain {''} @@ -171,6 +208,12 @@ InModuleScope MSFT_xComputer { Assert-MockCalled -CommandName Rename-Computer -Exactly 1 -Scope It Assert-MockCalled -CommandName Add-Computer -Exactly 0 -Scope It } + It 'Throws if name is to long' { + {Set-TargetResource -Name "ThisNameIsTooLong"} | Should Throw + } + It 'Throws if name contains illigal characters' { + {Set-TargetResource -Name "ThisIsBad<>"} | Should Throw + } } } } diff --git a/appveyor.yml b/appveyor.yml index 62d98c28..bbb2b1b5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,25 @@ +#---------------------------------# +# environment configuration # +#---------------------------------# +version: 1.4.{build}.0 install: - - cinst -y pester - - git clone https://github.com/PowerShell/DscResource.Tests + - cinst -y pester + - git clone https://github.com/PowerShell/DscResource.Tests + - ps: Push-Location + - cd DscResource.Tests + - ps: Import-Module .\TestHelper.psm1 -force + - ps: Pop-Location + +#---------------------------------# +# build configuration # +#---------------------------------# build: false +#---------------------------------# +# test configuration # +#---------------------------------# + test_script: - ps: | $testResultsFile = ".\TestsResults.xml" @@ -12,14 +28,37 @@ test_script: if ($res.FailedCount -gt 0) { throw "$($res.FailedCount) tests failed." } -on_finish: - - ps: | - $stagingDirectory = (Resolve-Path ..).Path - $zipFile = Join-Path $stagingDirectory "$(Split-Path $pwd -Leaf).zip" - Add-Type -assemblyname System.IO.Compression.FileSystem - [System.IO.Compression.ZipFile]::CreateFromDirectory($pwd, $zipFile) - @( - # You can add other artifacts here - (ls $zipFile) - ) | % { Push-AppveyorArtifact $_.FullName } + +#---------------------------------# +# deployment configuration # +#---------------------------------# + +# scripts to run before deployment +deploy_script: + - ps: | + # Creating project artifact + $stagingDirectory = (Resolve-Path ..).Path + $manifest = Join-Path $pwd "xComputerManagement.psd1" + (Get-Content $manifest -Raw).Replace("1.4.0.0", $env:APPVEYOR_BUILD_VERSION) | Out-File $manifest + $zipFilePath = Join-Path $stagingDirectory "$(Split-Path $pwd -Leaf).zip" + Add-Type -assemblyname System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::CreateFromDirectory($pwd, $zipFilePath) + + # Creating NuGet package artifact + New-Nuspec -packageName $env:APPVEYOR_PROJECT_NAME -version $env:APPVEYOR_BUILD_VERSION -author "Microsoft" -owners "Microsoft" -licenseUrl "https://github.com/PowerShell/DscResources/blob/master/LICENSE" -projectUrl "https://github.com/$($env:APPVEYOR_REPO_NAME)" -packageDescription $env:APPVEYOR_PROJECT_NAME -tags "DesiredStateConfiguration DSC DSCResourceKit" -destinationPath . + nuget pack ".\$($env:APPVEYOR_PROJECT_NAME).nuspec" -outputdirectory . + $nuGetPackageName = $env:APPVEYOR_PROJECT_NAME + "." + $env:APPVEYOR_BUILD_VERSION + ".nupkg" + $nuGetPackagePath = (Get-ChildItem $nuGetPackageName).FullName + + @( + # You can add other artifacts here + $zipFilePath, + $nuGetPackagePath + ) | % { + Write-Host "Pushing package $_ as Appveyor artifact" + Push-AppveyorArtifact $_ + } + + + diff --git a/xComputerManagement.psd1 b/xComputerManagement.psd1 index cd643fdf..e5889eb6 100644 --- a/xComputerManagement.psd1 +++ b/xComputerManagement.psd1 @@ -1,6 +1,6 @@ @{ # Version number of this module. -ModuleVersion = '1.3.0' +ModuleVersion = '1.4.0.0' # ID used to uniquely identify this module GUID = 'B5004952-489E-43EA-999C-F16A25355B89' @@ -30,5 +30,29 @@ FunctionsToExport = '*' # Cmdlets to export from this module CmdletsToExport = '*' + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResourceKit', 'DSCResource') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/PowerShell/xComputerManagement/blob/master/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/PowerShell/xComputerManagement' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + } # End of PSData hashtable + +} # End of PrivateData hashtable }