DSC Module does not implement the write property faceTime - dsc

I am not understanding the concept on how to create a Desired State Configuration Resource. I keep getting errors. I use the following. Can someone give me a very simple way to understand this?
Mof
[ClassVersion("1.0.0"), FriendlyName("SQLInstall")]
class MSFT_SQLInstall: OMI_BaseResource
{
[Key] String InstanceName;
[Write] String SA;
[Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure;
};
PSM1:
Function Get-TargetResource
{
param(
[parameter(Mandatory = $true)]
[System.String]
$InstanceName,
[ValidateSet('Present','Absent')]
[System.String]
$Ensure )
$ins = #($InstanceName)
return $ins
}
Function Set-TargetResource
{
param(
[parameter(Mandatory = $true)]
[System.String]
$InstanceName,
[ValidateSet('Present','Absent')]
[System.String]
$Ensure )
}
function Test-TargetResource
{
[OutputType([System.Boolean])]
param
(
[parameter(Mandatory = $true)]
[System.String]
$InstanceName,
[ValidateSet('Present','Absent')]
[System.String]
$Ensure
)
try {
write-verbose "Test: Getting current Instance $Instance status"
$Status = <# test goes here #>
if ($Ensure -like 'Present')
{
if (($Status -eq $true))
{
return $true
}
else
{
return $false
}
}
else
{
if ($Status -eq $true)
{
return $false
}
else
{
return $true
}
}
}
Catch {
$exception = $_
Write-Verbose ("An Error Occurred: $exception.message")
}
}

Your DSC resource is not complete. The PSM1 must have a Test-TargetResource function. Whenever a DSC resource is used, the Test-TargetResource runs first to check if the Set-TargetResource should be run or not.
You need to look at writing custom DSC resources help content on technet. Also, the DSC resource designer is a great starting point for beginners.

Related

How can I get invoke command to return an output to a variable?

I'm trying to loop through an array of servers and run this test path command as a background thread. It needs to be a thread because I will be monitoring it for a time out and killing the thread if it exceeds that time out.
I cannot get a variable return value, nor do I know how I would have separate variables for multiple machines or if I'm passing the variables properly. In c# I would use a thread collection, but I can't seem to figure out the syntax for Powershell.
foreach ($server in $servers)
{
Write-Host $server;
$scriptBlock =
{
$returnVal = Test-Path "\\$server\c$";
return $returnVal;
}
$remoteReturnVal = Invoke-Command -ComputerName [Environment]::MachineName -ScriptBlock { $script } –AsJob;
}
Here is how I can do it in Powershell with c# (the below works)
$shareCheck =
#'
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace powershellConsoleForm
{
public class shares
{
public List<string> shareExists(List<string> servers, int timeOut)
{
List<string> returnValue = new List<string>();
foreach (string server in servers)
{
string path = #"\\" + server + #"\c$";
Func<bool> func = () => Directory.Exists(path);
Task<bool> task = new Task<bool>(func);
task.Start();
if (task.Wait(timeOut))
{
//return task.Value;
Console.Write(" ");
Console.Write("success " + task.Result);
Console.Write(" ");
returnValue.Add(server + "|" + task.Result.ToString());
}
else
{
Console.Write("timeout");
returnValue.Add(server + "|timeout");
}
}
Console.Write("Done");
return returnValue;
}
}
}
'#
try
{
$shareChecker = New-Object powershellConsoleForm.shares;
}
catch
{
$assemblies = ("System", "System.Collections", "System.ComponentModel", "System.Data", "System.Drawing", "System.IO", "System.Linq", "System.Management.Automation", "System.Security", "System.Threading.Tasks", "System.Windows.Forms", "System.Threading", "System.Collections.Concurrent", "System.Security.Principal");
Add-Type -TypeDefinition $shareCheck -ReferencedAssemblies $assemblies -Language CSharp
$shareChecker = New-Object powershellConsoleForm.shares;
}
$servers = #("server1", "server2", "server3", "server4", "server5");
$timeOut = 11000;
[int] $counter = 0;
do
{
$counter ++;
Write-Host $counter;
$shareAvailibilities = $shareChecker.shareExists($servers, $timeOut);
foreach ($shareAvailibility in $shareAvailibilities)
{
Write-Host $shareAvailibility;
}
Write-Host " ";
Write-Host " ";
Sleep 5;
}while ($true)
First thing, Invoke-Command by defaults returns the output of the scriptblock. So your scriptblock could be something like $ScriptBlock = {Test-Path "\\$server\c$"}.
Then, there are several issues with your Invoke-Command line.
Since you're using -AsJob, you don't need to assign output to a variable. You would access the results of your job with Get-Job.
Then you could add -JobName $server so it's easier to view the results by server.
Remove -ComputerName as it runs on localhost by default. As a side note, I personally like using $env:COMPUTERNAME instead as it's a bit easier on the eyes, and to access.
You have wrapped your $script variable inside of a scriptblock, when it's already a script block. Remove the curly braces. But, since the command is so simple, it might make more sense to just keep the braces, and replace variable with the command.
here's a revised copy of your script, based on my adjustments (and a couple formatting tweaks):
foreach ($server in $servers) {
Write-Host $server
Invoke-Command -ScriptBlock { Test-Path "\\$server\c$" } –AsJob -JobName $server
}
Get-Job
I was thinking about it, and since you're just running these on the local machine, it would make a lot more sense to just use Start-Job:
foreach ($server in $servers) {
Write-Host $server
Start-Job -Name $server -ScriptBlock { Test-Path "\\$server\c$" }
}
Get-Job
Then to get the results of the jobs themselves you can use Receive-Job. Either pipe Get-Job to Receive-Job, or access each one by name Receive-Job -Name <servername>

How to call outside defined function in runspace scriptblock

I have a complex PowerShell function which I would like to run another threads.
However, If I'm right, the function cannot be accessed in the scriptblock. I want to avoid to copy every related function next to it.
Is there any way or method to call the function in the scriptblock?
function Function1 {
Param()
Process {
$param1 = "something"
$pool = [RunspaceFactory]::CreateRunspacePool(1, [int]$env:NUMBER_OF_PROCESSORS + 1)
$pool.ApartmentState = "MTA"
$pool.Open()
$runspaces = #()
$scriptblock = {
Param (
[Object] [Parameter(Mandatory = $true)] $param1
)
Complex_Function -param1 $param1
}
1..10 | ForEach-Object {
$runspace = [PowerShell]::Create()
$null = $runspace.AddScript($scriptblock)
$null = $runspace.AddArgument($param1)
$runspace.RunspacePool = $pool
$runspaces += [PSCustomObject]#{ Pipe = $runspace; Status = $runspace.BeginInvoke() }
}
while ($runspaces.Status -ne $null) {
$completed = $runspaces | Where-Object { $_.Status.IsCompleted -eq $true }
foreach ($runspace in $completed) {
$runspace.Pipe.EndInvoke($runspace.Status)
$runspace.Status = $null
}
}
$pool.Close()
$pool.Dispose()
}
}
function Complex_Function {
Param(
[Object] [Parameter(Mandatory = $true)] $param1
)
Process {
#several function calls
}
}
I think the code found in this blog post is probably what you're looking for:
Function ConvertTo-Hex {
Param([int]$Number)
'0x{0:x}' -f $Number
}
#Get body of function
$Definition = Get-Content Function:\ConvertTo-Hex -ErrorAction Stop
#Create a sessionstate function entry
$SessionStateFunction = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry
-ArgumentList 'ConvertTo-Hex', $Definition
#Create a SessionStateFunction
$InitialSessionState.Commands.Add($SessionStateFunction)
#Create the runspacepool by adding the sessionstate with the custom function
$RunspacePool = [runspacefactory]::CreateRunspacePool(1,5,$InitialSessionState,$Host)
Do something similar with your Complex_Function and (I guess) every other function you need and they should be usable by your runspaces.
edit
You asked in comments how to gather all functions. The path function:/ can be traversed and searched like a directory, so get-chiditem function:/ gets all currently-defined functions.
In experimenting with this, it seems as though functions that are defined within the current script, or from dot-sourced scripts, have an empty Source property. Play around with this. It should lead to what you want.
$InitialSessionState = [initialsessionstate]::Create()
Get-ChildItem function:/ | Where-Object Source -like "" | ForEach-Object {
$functionDefinition = Get-Content "Function:\$($_.Name)"
$sessionStateFunction = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry `
-ArgumentList $_.Name, $functionDefinition
$InitialSessionState.Commands.Add($sessionStateFunction)
}

Deleting empty Document Libraries in O365

I have an existing system that we are cleaning up with thousands of document libraries and moving the contents into Azure instead. I'm looking for a programmatic way to iterate over the lists to find ones that are empty and delete them. Does anyone have any samples (preferably using CSOM or powershell) to accomplish this that they would be willing to share?
At this point, I've come up with the following code to accomplish the task, however I'm getting the error "the underlying connection was closed: an unexpected error occurred on a receive" trying to load the lists due to a timeout because I have so many lists. Here's my solution so far hiding the user secrets of uid, pws, site.
var cred = new SharePointOnlineCredentials(uid, pws);
var context = new ClientContext(site);
context.Credentials = cred;
context.RequestTimeout = Timeout.Infinite;
var lists = context.Web.Lists;
context.Load(lists);
context.ExecuteQuery();
foreach(var list in lists)
{
list.DeleteObject();
list.Update();
}
Try the code below, all you have to do here is to specify the lists you want to ignore, some system lists are listed already, make sure you are not deleting important stuff from SharePoint.
The script below uses SharePoint Online Management Shell and PnP PowerShell, download and install both before running the script:
https://www.microsoft.com/en-us/download/details.aspx?id=35588
https://github.com/SharePoint/PnP-PowerShell
cls
$url = "https://YOUR-URL-GOES-HERE.sharepoint.com"
if ($cred -eq $null)
{
$cred = Get-Credential
Connect-PnPOnline $url -Credentials $cred
}
$CSOM_context = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$CSOM_credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($cred.UserName, $cred.Password)
$CSOM_context.Credentials = $CSOM_credentials
$lists = $CSOM_context.Web.Lists
$CSOM_context.Load($lists)
$CSOM_context.ExecuteQuery()
$ignoreList = "Form Templates", "Site Assets", "Style Library", "_catalogs/hubsite", "Translation Packages"
$lists | ? { $_.ItemCount -eq 0 -and $_.BaseTemplate -eq 101 -and $_.Title -inotin $ignoreList} | % {
Write-Host "- " $_.Title -NoNewline
try {
Remove-PnPList -Identity $_.Title -Force
Write-Host -ForegroundColor Green " [deleted] " `n
}
catch [Execption] {
Write-Host -ForegroundColor Red " [FAILURE] - " $_.Exception.Message `n
}
}
I don't think so you need any timeout over here you can use below code to delete an empty document library
using Microsoft.SharePoint.Client;
using System;
using System.Linq;
using System.Security;
namespace DeleteAllEmptyDocumentLibrary
{
class Program
{
static void Main(string[] args)
{
ListItemCollection itemCollection = null;
SecureString pswd = new SecureString();
try
{
// Site Url to scan
string webUrl = "https://SiteUrl";
string userName = "UserName";
string password = "Password";
using (ClientContext context = new ClientContext(webUrl))
{
foreach (char c in password.ToCharArray())
pswd.AppendChar(c);
// Setting credential for the above site
context.Credentials = new SharePointOnlineCredentials(userName, pswd);
context.ExecuteQuery();
var lists = context.LoadQuery(context.Web.Lists.Where(l => l.BaseType == BaseType.DocumentLibrary));
context.ExecuteQuery();
foreach (List list in lists)
{
try
{
// Getting all items from selected list using caml query
itemCollection = list.GetItems(CamlQuery.CreateAllItemsQuery());
//Loading selected list items
context.Load(itemCollection);
context.ExecuteQuery();
// Looping each item
if (itemCollection != null && itemCollection.Count == 0)
{
list.DeleteObject();
context.ExecuteQuery();
}
}
catch (Exception ex)
{ }
}
}
}
catch (Exception ex) { }
}
}
}

Custom powershell cmdlets do not support multi-threading?

In the code blocks below, I'm trying to run Get-MyCmdlet in 3 separate threads, each of which opens a google page if the Get-MyCmdlet did not give anything.
The Get-MyCmdlet is fairly simple, the only thing it does is WriteObject("hello world"); (defined in the c# code).
However the script always opens up a google page, unless I change the Get-MyCmdlet to Get-Host(which is a system default cmdlet).
Is it because the custom cmdlets are not supporting multi-threading? Any help will be greatly appreciated!
The cmdlet:
[Cmdlet(VerbsCommon.Get, "MyCmdlet ")]
public class GetMyCmdlet : Cmdlet
{
protected override void ProcessRecord()
{
WriteObject("hello world");
}
}
the script:
$ScriptBlock = {
$result = Get-MyCmdlet
if (!$result) {[System.Diagnostics.Process]::Start("http://www.google.com")}
$name = ($result | get-member)[-1].name
$result = $result.$name
return $result
}
....
$threads = 3
for ($i = 0; $i -lt $threads) {
$running = #($jobs | Where-Object {$_.State -match 'Running'})
Write-Host $running.length
if ($running.length -lt $threads) {
$jobs += Start-job -ScriptBlock $ScriptBlock
$i = $i + 1
} else {
get-job | receive-job
$finished = #($jobs | Where-Object ($_.State -match 'Completed'))
if ($finished) {
$finished.getType()
foreach($job in $finished) {
Receive-Job -keep $job | Out-File "Output$.txt"
$i = $i + 1
$finished.Remove($job)
Remove-Job -job $job
}
}
}
}
No custom cmdlets are not the problem. My guess is the problem is that calling Get-MyCmdlet fails with an error so $result is not set and thanks to your if the browser is started. If you would check the results of your jobs you would see an error message. You probably need to make sure the job initializes right so you can call your cmdlet. You could use the -InitializationScript parameter of Start-Job to import your module for the job. See here, here and here for more information.

format powershell output to like table format

I have this code below and I would like to display the output in a table like fashion
$spSite = Get-SPSite "http://sp2010"
$spWeb = $spSite.OpenWeb()
$spList = $spWeb.Lists["ReportList"]
$items = $spList.Items
foreach($item in $items)
{
Write-Host $item["ID"] $item["Name"] $item["Department"] $item["Telephone"]
}
like this:
ID Name Department Telephone
--- -------- ----------- ------------
1 Somone IT 02554445588
I saw Format-Table, but I cannot seem to get the hang of it.
Some example using the code above will be very much appreciated.
Thanks a lot.
Hard to even start helping, because current code simply won't work... ($spListItem is... what? foreach uses $item, can we assume you are using it?). Also: we have very little to no information about type of this items, it looks like some dictionary object... Anyway, I would try:
$items | ForEach-Object {
# assume $items is array of dictionary objects...
New-Object PSObject -Property $_
} | Format-Table ID, Name, Department, Telephone
obviously, it may not work, because I made some assumptions that can be just wrong.. ;)
Give this a try:
function Get-SPListItem
{
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[Microsoft.SharePoint.SPList]$List,
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]$ViewName
)
process
{
try
{
if($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('ViewName'))
{
$views = $list.Views | Select-Object -ExpandProperty Title
if($views -contains $ViewName)
{
$columns = $List.Views[$ViewName].ViewFields
}
else
{
throw 'Invalid view name'
}
}
else
{
$columns = $List.DefaultView.ViewFields
}
foreach($item in $List.Items)
{
$obj = New-Object PSObject
foreach($col in $columns)
{
$obj | Add-Member -MemberType NoteProperty -Name $col -Value $item[$col]
}
$obj
}
}
catch
{
Write-Error $_
}
}
}
$spWeb.Lists["ReportList"] | Get-SPListItem

Resources