Using Functions with Optional and Required Parameters

Just a quick example of how to pass parameters to a powershell function. This works also as script parameters…

#——————–
function test{
Param($inOptional1=”Hi”,$inRequired1=$(throw “inRequired1 is a required input”),$inOptional2=$null)
Write-Host $inOptional1
Write-Host $inRequired1
Write-Host $inOptional2
}
“——————-”
test “Hello” “World” “Lovely day”
“——————-”
test -inOptional1 “Hello” -inRequired1 “World”
“——————-”
test -inOptional1 “Hello” -inRequired1 “World” -inOptional2 “Lovely day”
“——————-”
test -inRequired1 “World”
“——————-”
test -inOptional1 “Hello”
“——————-“

Advertisement

Adding Roles and Features in SCCM Task Sequence

Should be simple. And I have done it before and it was, dead simple. This post kinda overlaps with running powershell scripts, batch files, and command lines from Task Sequence, and the example here is Roles and Features.

Sticking with deploying Server 2008 R2 for simplicity, I want to add Roles and Features after the OS is installed: .NET Framework, and RDS, for XenApp Servers.

Previously, you could use ServerManagerCmd.exe for this, so when deploying 2008 or earlier then you need to modify accordingly. ServerManagerCmd is deprecated in R2 so you need to use Powershell, and the servermanager module.

First thing is to set the exec policy: i use a command-line step in the Task Sequence:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noprofile -command “&{Set-ExecutionPolicy Unrestricted -Force}”

But you can also use a command line with Reg:

reg add HKLM\Software\Microsoft\Powershell\1\ShellIds\Microsoft.Powershell /v ExecutionPolicy /t REG_SZ /d Unrestricted /f

Of course you need to run from command line for the posh variant because you can’t use a powershell script to set the execution policy when the policy already restricts scripts from running – a chicken and egg analogy. Incidentally, the egg came first. A reptile egg presumably, but it hatched into a genetic mutation which in turn laid a mutant egg and this process eventually led to to the chicken being hatched from a non-chicken egg. So perhaps the chicken did come first then if you mean which came first, chicken or chicken’s egg? Otherwise it’s egg. Maybe better to think how you might get your car keys out of your locked car. That’s a simpler conundrum.

Anyway, onward. Another command-line:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noprofile -command “&{Import-Module -Name C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ServerManager\servermanager.psd1;Add-WindowsFeature AS-NET-Framework -includeAllSubFeature}”

The option with this last command-line is that you can put it in a script with a whole host of other post install tasks or changes, package it and call from the task sequence. Obviously your script only needs the part in the “{}”. Interestingly I was getting errors last time with paths, hence the full paths to the exe and psd1, but it does work without these so long as working dir etc is correct.

So that’s a pretty easy way to install a role or feature. But you could also use the MDT integration with SCCM to create a MDT Task Sequence.

For MDT you need to configure the MDT integration, and create an MDT Tools Package: when you create a new MDT TS you can create the Tools package in the wizard.

Be careful though, for some reason if you use a standard SCCM TS and add MDT steps they don’t always work. It’s easier to create a whole MDT TS and modify it accordingly, particularly for Roles and Features. For installing Language Packs, you can use a standard SCCM TS and add the Use MDT Toolkit step, then Language Pack Offline step and it works fine. Not so with Roles/Features. Funny.

Err on the side of caution I say, create the MDT TS and remove the extraneous steps and groups. It’ll save you time. Time enough to figure out how to get your keys…

SCCM Add Computer Associations with Powershell – Download Executable

Ok, here is an exe version of this tool. The code inside is also a bit tidier.
Download the zip file from here:
https://sites.google.com/site/andrewcraigsccm/ACATSV409.zip

Extract and copy the files to a location of choice but make a note of where.
Run the exe from a powershell or command window, or create a shortcut. You must use the following command line syntax:

powershell.exe [pathToExecutable] -Arguments -ConfigFile [pathToXML]
For Example:
powershell.exe F:\scripts\sccm\ACATSV409.exe -Arguments -ConfigFile “F:\scripts\SCCM”

When run from a powershell window of course you don’t need powershell.exe at the front.
At first run you will get a message to insert site provider (fqdn of sccm site server) and then site code (xxx).

That’s it, have fun. The IP Address and Firmware functions on the front page just set Machine Variables – in order to use these you need to create Task Sequences with Conditions.

SCCM Add Computer Associations With Powershell – Code

Below is the source code for this tool. I would like to compile it a bit better so it is more dynamic: it uses an ini file called acatsv4t.ini which should really be in a proper ini format with tags to allow config of the available options, for example available language and OS. Once I have the ini config sorted i’ll compile as an exe, with an install/self-extract routine.

Code is provided as is. No official support provided and i accept no responsibility for misuse. Other than that, hope it comes in handy.

Andy
addComputerAssociationToSCCM_v401_Tabbed.ps1

Create Excel Reports in Powershell

Create Excel Reports with nice Graphs in Powershell.

ConfigMgr has plenty of scope in the Reporting Point to produce nice reports on everything SCCM.
You can also publish these reports in Sharepoint or other dashboard.
Or you can use Reporting Services for SQL.

All of which are great but managers are all different, and all the same. What they want are graphics and charts, an overview of status, not too much detail, but they want to see that everything is going well and that you have put in a bit of effort to produce something presentable. This last point is universally accepted as an existence-justification and plays sadly a large part in any job today. So how to demonstrate just how industrious you are?

Answer: Make lovely looking charts and graphs and present them…

At the moment we don’t have SQL Reporting Services, and I want to focus on setting it up, making some cool dashboards. This means I need some time away from producing charts and graphs and powerpoints. And SCCM Reporting only does 2D graphs, not as pretty as managers expect.

Answer 2: build a script to produce nice looking excel-based reports and charts for me. I’ll still need to present, I haven’t found a way of scripting that yet, but I’ll have more time to focus on my work.

This is one variation, based on using what is available. That is, Excel. Visio is also possible, as is using SMTP to email the report to your boss, or automatically print out before a status review meeting.

Here is the script (in Powershell of course):

clear
#————————————————————————–
$script:SQLServer = “”
$script:SQLDBName = “”
$excel = New-Object -ComObject Excel.Application
$workbook = $excel.workbooks.add()
$chartType = “Microsoft.Office.Interop.Excel.xlChartType” -as [type]
$j = 1
$outputFile = ‘c:\Temp\PatchReview\PatchReview.xlsx’
$filter = ‘%Patch-Windows-Updates%’
#————————————————————————–
if(!(test-path c:\Temp\PatchReview\TablesCache)){
New-Item c:\Temp\PatchReview\TablesCache -ItemType Directory
}
#————————————————————————–
function RunSQLQuery($SQLQuery,$TableName){
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection $SqlConnection.ConnectionString = “Server = $script:SQLServer; Database = $script:SQLDBName; Integrated Security = True”
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
BuildTable $DataSet $TableName
}
#————————————————————————–
function BuildTable($DataSetIn,$TableName){
$TableObject = New-Object System.Data.DataTable $TableName
foreach($Column in $DataSetIn.Tables[0].Columns){
$TableObject.Columns.Add((New-Object system.Data.DataColumn $Column.ColumnName,([string])))
}
foreach($Row in $DataSetIn.Tables[0].Rows){
$newRow = $TableObject.NewRow()
for($i=0;$i -lt $DataSetIn.Tables[0].Columns.Count;$i++){
$newRow.($DataSetIn.Tables[0].Columns[$i].ColumnName) = $Row[$i]
}
$TableObject.Rows.Add($newRow)
}
if($TableName -like “Compliance Summary – Patch-Windows-Updates-*”)
buildReport $TableObject $TableName
}
if($TableName -eq “UpdateList”){
BuildAssignmentsReport $TableObject
}
if($TableName -eq “Insgesamt”){
BuildInsgesamtReport $TableObject ́
}
$TableObject | Export-Clixml “c:\Temp\PatchReview\TablesCache\$TableName.xml” -Force
}
#————————————————————————–
function buildReport($Data,$TableName){
$ChartName = $TableName -replace “Compliance Summary \- Patch\-Windows\-Updates\-”
$ChartName = $ChartName -replace “\ \- \(01\.2012\)”
if($j -gt 3){
$workbook.Worksheets.Add([System.Reflection.Missing]::Value,$excel.Worksheets.Item($excel.Worksheets.count)).Name = $ChartName
}
}else{
$workbook.Worksheets.item($j).Name = $ChartName
}
$sheet = $workbook.Worksheets.item($ChartName)
$x = 2
$sheet.cells.item(1,1)= “LastEnforcementState”
$sheet.cells.item(1,2)= “NumberOfComputers”
$sheet.cells.item(1,3)= “PercentageComputers”
Foreach($row in $Data)
{
$sheet.cells.item($x,1) = $row.LastEnforcementState
$sheet.cells.item($x,2) = $row.NumberOfComputers
$sheet.cells.item($x,3) = $row.PComputers
$x++
}
$range = $sheet.usedRange
$range.EntireColumn.AutoFit()
$chart = $workbook.charts.add()
$chart.chartType = $chartType::xl3DPie
$chart.HasTitle = $true
$chart.ChartTitle.Text = $ChartName
$workbook.ActiveChart.SetSourceData($range)
$workbook.ActiveChart.Location(2,$($sheet.name))
$workbook.ActiveChart.Parent.Height = 300
$workbook.ActiveChart.Parent.Width = 400
$workbook.ActiveChart.Parent.Top = 200
$workbook.ActiveChart.Parent.Left = 0
$j++
}
#————————————————————————–
function BuildAssignmentsReport($Data){
$workbook.Worksheets.Add([System.Reflection.Missing]::Value,$excel.Worksheets.Item($excel.Worksheets.count)).Name = “ApplicableUpdates”
$sheet = $workbook.Worksheets.item(“ApplicableUpdates”)
$x = 2
$sheet.cells.item(1,1) = “BulletinID”
$sheet.cells.item(1,2) = “ArticleID”
$sheet.cells.item(1,3) = “Title”
$sheet.cells.item(1,4) = “Description”
Foreach($row in $Data)
{
$sheet.cells.item($x,1) = $row.BulletinID
$sheet.cells.item($x,2) = $row.ArticleID
$sheet.cells.item($x,3) = $row.Title
$sheet.cells.item($x,4) = $row.Description
$x++
}
$range = $sheet.usedRange
$range.EntireColumn.AutoFit()
}
#————————————————————————–
function BuildInsgesamtReport($Data){
$workbook.Worksheets.Add([System.Reflection.Missing]::Value,$excel.Worksheets.Item($excel.Worksheets.count)).Name = “Insgesamt”
$sheet = $workbook.Worksheets.item(“Insgesamt”)
$ChartName = “Insgesamt”
$x = 2
$sheet.cells.item(1,1) = “Compliance State”
$sheet.cells.item(1,2) = “Hosts”
Foreach($row in $Data)
{
$sheet.cells.item($x,1) = $row.StateName

$sheet.cells.item($x,2) = $row.Hosts
$x++
}
$range = $sheet.usedRange
$range.EntireColumn.AutoFit()
$chart = $workbook.charts.add()
$chart.chartType = $chartType::xl3DPie

$chart.HasTitle = $true
$chart.ChartTitle.Text = $ChartName
$workbook.ActiveChart.SetSourceData($range)

$workbook.ActiveChart.Location(2,$($sheet.name))
$workbook.ActiveChart.Parent.Height = 300
$workbook.ActiveChart.Parent.Width = 400
$workbook.ActiveChart.Parent.Top = 200
$workbook.ActiveChart.Parent.Left = 0
}
#————————————————————————–
RunSQLQuery “SELECT dbo.v_CIAssignment.AssignmentName, dbo.v_CIAssignment.Assignment_UniqueID, dbo.v_CIAssignment.StartTime,
dbo.v_CIAssignment.EnforcementDeadline, dbo.v_Collection.Name, dbo.v_CIAssignment.AssignmentID
FROM dbo.v_CIAssignment INNER JOIN
dbo.v_Collection ON dbo.v_CIAssignment.CollectionID =
dbo.v_Collection.CollectionID
WHERE (dbo.v_CIAssignment.AssignmentName LIKE ‘%$filter%’)” “CIAssignmentTable”
#————————————————————————–
RunSQLQuery “SELECT CollectionID, Name, Comment
FROM dbo.v_Collection” “CollectionTable”
#————————————————————————–
#————————————————————————–
$CIAssignmentTable = Import-Clixml c:\Temp\PatchReview\TablesCache\CIAssignmentTable.xml
foreach($Row in $CIAssignmentTable){
$DeploymentName = $Row.AssignmentName
$DEPLOYMENTID = $Row.Assignment_UniqueID
$strSQL = “declare @DeploymentLocalID as int
declare @COLLCOUNT as int
declare @DEPLOYMENTID as nvarchar(50)
set @DEPLOYMENTID = ‘$DEPLOYMENTID’
select @DeploymentLocalID = AssignmentID from v_CIAssignment where Assignment_UniqueID = @DEPLOYMENTID
select @COLLCOUNT=count(*) from v_CIAssignmentTargetedMachines where AssignmentID=@DeploymentLocalID
select
a.Assignment_UniqueID as DeploymentID,
a.AssignmentName as DeploymentName,
a.StartTime as Available,
a.EnforcementDeadline as Deadline,
sn.StateName as LastEnforcementState,
count(*) as NumberOfComputers,
PComputers=convert(numeric(5,2), (isnull(count(*), 0)* 100.00 / isnull(nullif(@COLLCOUNT, 0), 1))),
sn.TopicType*10000 + sn.StateID as DeploymentStateID from v_CIAssignment a
join v_AssignmentState_Combined assc
on a.AssignmentID=assc.AssignmentID join v_StateNames sn
on assc.StateType = sn.TopicType and sn.StateID=isnull(assc.StateID,0)
where (@DEPLOYMENTID is null or a.Assignment_UniqueID = @DEPLOYMENTID)
and assc.StateType in (300,301)
group by a.Assignment_UniqueID,a.AssignmentName, a.StartTime, a.EnforcementDeadline, sn.StateName, sn.TopicType, sn.StateID
order by sn.StateName”
RunSQLQuery $strSQL “Compliance Summary – $DeploymentName”
}
#————————————————————————–
$AssignmentList = $null
foreach($Row in $CIAssignmentTable){
$AssignmentList = $AssignmentList + $Row.AssignmentID + ‘,’
}
$AssignmentList = $AssignmentList.TrimEnd(‘,’)
RunSQLQuery “SELECT sn.StateName,(ac.StateType*10000 + isnull(ac.StateID,0)) AS [StateID],COUNT(ac.ResourceID) AS [Hosts] FROM
v_AssignmentState_Combined ac
JOIN v_StateNames sn ON (sn.TopicType*10000 + sn.StateID) = (ac.StateType*10000 + ISNULL(ac.StateID,0))
WHERE ac.AssignmentID in ($AssignmentList)
GROUP BY sn.StateName,(ac.StateType*10000 + ISNULL(ac.StateID,0))
ORDER BY sn.StateName” “Insgesamt”
#————————————————————————–
RunSQLQuery “SELECT DISTINCT dbo.v_CIAssignmentToCI.CI_ID, dbo.v_UpdateInfo.BulletinID, dbo.v_UpdateInfo.ArticleID, dbo.v_UpdateInfo.Title,
dbo.v_UpdateInfo.Description
FROM dbo.v_CIAssignment INNER JOIN
dbo.v_CIAssignmentToCI ON dbo.v_CIAssignment.AssignmentID = dbo.v_CIAssignmentToCI.AssignmentID INNER
JOIN
dbo.v_UpdateInfo ON dbo.v_CIAssignmentToCI.CI_ID =
dbo.v_UpdateInfo.CI_ID
WHERE (dbo.v_CIAssignment.AssignmentName LIKE ‘%$filter%’)” “UpdateList”
#————————————————————————–
#————————————————————————–
$excel.visible = $true
$hwnd = $excel.Hwnd
#————————————————————————–
$excel.DisplayAlerts = $false
$workbook.SaveAs($outputFile)
#————————————————————————–
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
Get-Process -Name EXCEL | Where-Object {$_.MainWindowHandle -eq $hwnd} | Stop-Process
#————————————————————————–

Example Report: PatchReview

MDT 2010 – Using The Database And Powershell To Enhance Administration

It is possible to build some automation into your MDT environment firstly by using the Advanced Configuration available and configuring a database (SQL Express for instance to keep costs down), and secondly start manipulating this database with powershell to create bulk-actions, populations, record changes etc etc…

First place to start is with Michael Niehaus. Back in 2009 Michael published this powershell module for use with MDT: Read about the cmdlets and download the module here: http://blogs.technet.com/b/mniehaus/archive/2009/05/15/manipulating-the-microsoft-deployment-toolkit-database-using-powershell.aspx

Once you’ve downloaded and saved the psm1, open powerhell and set the execution policy to unrestricted: “set-executionpolicy unrestricted” and accept the warning:

Now import the psm1 module by typing “import-module %pathtoscript%\MDTDB.psm1 -verbose”

First task is to connect to the MDT database, using the Connect-MDTDatabase cmdlet. Use “get-help connect-mdtdatabase” to check the syntax but it’s basically:

“Connect-MDTDatabase -SQLServer ServerName -Instance InstanceName -Database DatabaseName

Then you’re ready to go.

Useful cmdlets: Get-MDTComputer, New-MDTComputer, Set-MDTComputer

Try using a csv, and an import-csv cmdlet to bulk populate your database with computers:

New-MDTComputer –macAddress MACAddress –settings @{OSInstall=’YES’}

The first thing I wanted to do after populating was control which computers could be installed via F12. I set the OSINSTALL attribute in the Default Section of CustomSettings.ini to OSINSTALL=NO and set the priority of Default to last.

Then I used Get-MDTComputer | Set-MDTComputer -Settings @{OSINSTALL=’YES’} to allow all machines to build.

Once they were built, it was easy to use Get-MDTComputer | Set-MDTComputer -Settings @{OSINSTALL=”} to stop them from being able to rebuild should someone decide they wanted to do so without authorisation.

Get ConfigMgr Machine Variables With Powershell

Machine Variables can be retrieved from computer objects in SCCM with the Powershell script at the bottom of the page.

Variables in SCCM can be very useful: Task Sequence Variables, Collection Variables, Machine Variables. I use Machine Variables to identify specific settings such as which Language to install the OS with – this one is pretty useful when you consider that UK, France and German-speaking countries, for a start, all have slight differences in keyboard layout.

I’m a big fan of complexity. I like massively over-engineered solutions for small tasks. The point is that even a motorised egg-cooker and alarm clock system such as Doc Brown’s in Back to the Future, offsets the overhead of required initial engineering against the savings made over time – it is just a matter of time before an automated task like this wins the efficiency battle. Yes, it takes 10 minutes for you to crack eggs in a pan and cook them and slap them onto a piece of bread, but it is ten minutes every time, of your time. If like me you could happily eat an egg sandwich every morning, that equates to 70 minutes per week, 60 hours in one year. I reckon Doc Brown would take less than 60 hours to conceive and build an egg-sandwich machine, lets say 30 hours, that leaves him 30 hours more free time in the first year and 60 every year thereafter. And the egg sandwich is always the same.

So building a complex solution can save time and increase efficiency. That is all.

What has this to do with Machine Variables? Not a lot except that building scripts which can automate tasks is a bit like building an egg-sandwich machine. When you then build the scripts to work against variables, or to generate variables and then consequently re-reference themselves against the generated variables, then you start to look at a pretty complex solution for clicking the screen that asks you which language to install an OS in. But this screen doesn`t appear straight away in OSD, so you would either wait for it, or go away and come back. Eventually a scripted, complex solution wins the efficiency argument, trust me it`s just a matter of time.

So, I have a script which writes machine variables to a computer object in SCCM. I want the script to reference these variables later, how do I get the variables and values?

In the Windows.SMS.Environment you can retrieve the SCCM variables on the local machine.

You could use remoting to get the variables in the same way.

But I want to reset a LastPXEAdvertisement according to flag set in a variable and the machine isn’t switched on. So I could open the SCCM console and find the machine, right-click and check the flag, then reset the PXEAdvert, for example. Then write another variable to say it has been reset.

I think I have gone on long enough about why automate, and why build complexity…. Here is a script to pull back the Machine Variables for a computer object from SCCM using Powershell:

objComputer = gwmi -computername “$smsProvider” -namespace “root\sms\site_$siteCode” -class “sms_r_system” | where{$_.Name -eq $computerName}

$objMachineSettings = gwmi -computername “$smsProvider” -namespace “root\sms\site_$siteCode” -class “sms_machinesettings” | where{$_.ResourceID -eq $objComputer.ResourceID}

$objMachineSettings.get()

$variable = ($objMachineSettings.MachineVariables | where{$_.Name -eq “$desiredVariable”}).Value

And that, aside from script variables which is something else entirely, is a script which will return the value of a specific variable. Use the SMS_MachineSettings Class and then populate the fields with the GET() command and there you are. Simple. But then complex machines are built of many, many simple parts. The trick is how to put them together.

Hope it helps someone, I spent a bit of time wondering why the MachineVariables field was empty until I realized I needed the second Get expression.

Time now for a nice, uncomplicated weekend….

SCCM Add Computer Associations With Powershell

I can’t wait to get ConfigMgr 2012 up and running. Beta 2 is going into my lab as i type…

Until then…. Yes, ConfigMgr 2007 is fantastic, i love it. But i deploy it at customer sites and as a rule the technicals who are to run and operate it once i have gone have not taken any form of SCCM training. With 2007’s MMC Console it can be a bit daunting for them, so much information, and the access security is not what it should be. So until the launch of 2012 with it’s lovely interfaces and user-friendly-centric operations how can i enable my customers to carry out day-to-day tasks in SCCM?

First of all identify the key stakeholders: Who wants SCCM? Who needs SCCM? Why do they want it? What role in the business do they expect it perform?

FD: Paying for this new technology, wants return on investment.

IT Manager or similar: Probably wants deployment processes streamlined for their staff, to enable them to work more efficiently. Wants to know what IT equipment and licensing they have. Doesn’t care about the technical side.

IT Technician: More interested in the technical aspects. May want to get under the hood, but without training not advisable. Has (hopefully) documented the deployment and operational processes they want to streamline. Knows the applications, the estate, the environment.

For FDs and IT Management, the built-in Reports may well be enough to get information on the estate and inventory, with R2’s Reporting Services Point enhancing this functionality.

For Technicians, those who will be running and maintaining the SCCM environment and carrying out the deployment and administrative tasks with SCCM the MMC console can be a maze of nodes and components.

I identified a need for a customer to carry out basic tasks, such as Import Computer Information. They wanted to quickly add in new hardware to receive OSD Task Sequence Advertisements via PXE. I created a simple tool in Powershell which they could run from their desktop and easily add a new laptop or computer for OS deployment.

First of all, and i won’t go into too much detail here, i set up an OSD Task Sequence, a Collection and advertised the TS. 

In normal circumstances, the technician would open the SCCM console, find the collection with the TS advert and drill-down through the OSD Node, to import the computer using the wizard.

All these steps are possible with WMI.

 The basic powershell to add a computer account to SCCM:

#create Computer Account in SCCM

$siteClassString=”\\$siteProviderName\root\sms\site_$siteCode”+”:SMS_Site”

$siteClass=[WmiClass]$siteClassString

$methodAddEntry=”ImportMachineEntry”

$inParamsAddEntry=$siteClass.psbase.GetMethodParameters($methodAddEntry)

$inParamsAddEntry.MACAddress =$mACAddress.Text

$inParamsAddEntry.OverwriteExistingRecord =$false

$inParamsAddEntry.NetbiosName =$serverName.Text

$objComputer=$siteClass.psbase.InvokeMethod($methodAddEntry, $inParamsAddEntry, $null)

Next add the direct collection membership rule, this always applies to the collection and not the computer:

$objCollRuleDirectString=”\\$siteProviderName\root\sms\site_$siteCode”+”:SMS_CollectionRuleDirect”

$objCollectionRuleDirect=[WmiClass]$objCollRuleDirectString

$objCollectionRuleDirect.psbase.Properties[“ResourceClassName”].Value = “SMS_R_System”

$objCollectionRuleDirect.psbase.Properties[“ResourceID”].value = $objComputer.ResourceID

$methodAddToCollection=”AddMembershipRule”

And add the computer to the collection using this rule:

$targetCollection=Get-WmiObject -ComputerName “$siteProviderName” -Namespace “root\sms\site_$siteCode” -Class “SMS_Collection” | where{$_.Name -eq $determinedCollection}

$CollectionID=$targetCollection.CollectionID

$inParamsAddToCollection=$targetCollection.psbase.GetMethodParameters($methodAddToCollection)

$inParamsAddToCollection.collectionRule=$objCollectionRuleDirect

$addToCollectionResult=$targetCollection.psbase.InvokeMethod($methodAddToCollection, $inParamsAddToCollection, $null)

I also used Computer Variables to designate if the computer is to be installed with german, french or italian regional settings and keyboard layouts, this is useful when working in Switzerland. First you create an empty array of machine variables:

#create machine task sequence variables

$machineClassString=”\\$siteProviderName\root\sms\site_$siteCode”+”:SMS_MachineSettings”

$machineClass=[WmiClass]$machineClassString

$computerInstance = $machineClass.CreateInstance()

$computerInstance.psbase.properties[“ResourceID”].value=$objComputer.ResourceID

$computerInstance.psbase.properties[“SourceSite”].value=$siteCode

$machineVariablesString=”\\$siteProviderName\root\sms\site_$siteCode”+”:SMS_MachineVariable”

for($i=0;$i-lt9;$i++)

{

$computerInstance.MachineVariables=$computerInstance.MachineVariables+[WmiClass]$machineVariablesString

}

$machineVariables=$computerInstance.MachineVariables

Then you can add variables, i use a Windows Form object to take all this information in from input:

$machineVariables[0].name=”LanguageID”

$machineVariables[0].value=$languageID.SelectedItem.ToString()

I know i have a left a lot out here, and i will skip quickly to the end, but the important thing i have found is that once you harness WMI and powershell, with windows form objects, you can create all manner of dazzling(?) tools to help make SCCM life a bit easier.

Here is a couple of screengrabs of my tool:

For client management, Roger Zander’s Client Center is still freely available from sourceforge:

http://sourceforge.net/projects/smsclictr/

And for another method to set regional settings in a task sequence, i found this post very interesting:

http://www.myitforum.com/myITToolbar/frame-click.asp?url=http://ninet.org/2010/11/osd-customising-deployment-depending-on-location/

Installing SCCM R3 Clients

In order to fully utilise the Power-Management functionality of SCCM R3 you will need to upgrade the clients using the microsoft KB977384 http://support.microsoft.com/kb/977384/en-us

This latest hotfix includes support for ConfigMgr with International Client Pack.

However when you push Clients from SCCM R3, using the included Client install files you will still find that you have Client version 4.00.6487.2000 (or 4.00.6487.2700 if you have ICP).

The new Client version is 4.00.6487.2157 (or 4.00.6487.2857 with ICP).

So after installing new clients in your R3 environment you will still need to push the hotfix. I tried putting the patch in the ‘patch’ folder of the ConfigMgr Client installation source, and tried using the OSD Task Sequence.

The worst thing that happened when using as part of a Task Sequence is if you are deploying to an off-domain machine, and using ConfigMgr Client installation parameters in the original package, then the parameters get lost when the hotfix reinstalls the client, leaving you with errors in the LocationServices.log about failing to find the SLP.

So the choices are testing with the patch folder or the “patch=” parameter or rolling out the client after the machines have been deployed and have joined the domain, providing you are AD-integrated. I prefer the latter, although it seems a little long-winded I know I won’t have a problem in OSD, and I don’t really need R3 functionality during the OSD phase.

In any case, I need to roll out software updates after the OSD phase anyway because the Install Software Updates section for Windows 7 isn’t working properly. Waiting on Microsoft for that one…

http://blogs.technet.com/b/configmgrteam/archive/2011/01/28/known-issue-install-software-updates-action-hangs-on-windows-7.aspx

Set PoSh ExecutionPolicy For SCCM TS

I have some powershell scripts to run as part of an OSD Task Sequence, to configure things like Power Plans and Optical Drive lettering for instance.
The first thing I want to do is set the execution policy on the target machine to RemoteSigned.

How do you remotely change the execution policy on a machine when you cannot run a script against it?

You can use the input parameters of the powershell.exe (EXE):

use the command switch to input a script block.

powershell.exe -noprofile -command “& {set-executionpolicy remotesigned -force}”

Use ‘noprofile’ to run without loading a powershell profile and the ‘-force’ switch to suppress the confirmation request.

For SCCM, create a Software Distribution package containing the scripts, then create a program with the command line as above.