none
Deploying Powershell scripts via SCCM

    Question

  • I used to deploy .ps1 files from SMS 2003 successfully using the following command:

    powershell -command .\script.ps1

    unfortunatly its just failing after the allowed elapsed time specified in application. I have the script execution policy set on the machine as RemoteSigned. I have read the following blog about deploying to x64 and powershell referancing SysWoW64.

    If i run the command remotly from my machine it works fine. But deploying from SCCM isnt.. I have tried changing the execution policy in the command to:

    powershell.exe -executionpolicy bypass -file .\script.ps1 and still doesnt work...

    Has anyone got any experiance with deploy powershell scripts through sccm?

    Monday, February 13, 2012 2:16 PM

Answers

  • Hi,

    We powershell scripts for nearly every SCCM software deployment. Every app command line looks like ....

    "powershell.exe -command .\install.ps1"

    We have a internal CA, sign all our code and have those code signing certs pushed out via AD and in our image so are station builds can use our ps1 files.

    The only issue you might incounter which i think is a standard SCCM "thing" - make sure you aren't running any code with "write-host"

    A sample Office install ...

    $Path = "c:\bin\" $ProgessLog = "Office14Install.txt" Function Log-Message(){ Param($Message = ".") Write-Verbose $Message Write-Output $Message | Out-File "$Path$ProgessLog" -Append -Force } $Source = $pwd If (Test-Path "C:\Program Files (x86)"){ $PF="C:\Program Files (x86)" } Else { $PF="C:\Program Files" } Log-Message "Office14 Installation - The Present Working Directory is $pwd" $Products = @(get-wmiobject -class "Win32_Product") $OfficeFlag = @($Products | Where-Object {$_.Name -like "Microsoft Office Professional Plus 2010"}) If ($OfficeFlag){ $cmd = 'cmd /C ' + $Source + '\source\setup.exe /repair ProPlus /config ' + $Source + '\source\ProPlus.WW\SilentRepairConfig.xml' } Else{ $cmd = 'cmd /C ' + $Source + '\source\setup.exe' } Log-Message "Office14 Installation cmd is set to $cmd" Invoke-Expression $cmd | Out-Null Log-Message "Office14 Installation - Command execution finshed - Exit Code: $LASTEXITCODE" Remove-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "BCSSync" -Force

    Do you use SCCM Client Control Center? I find the execution history is really useful for finding out why an app has failed to install.

    Fingers crossed it works better for you a second time.

    Thanks,

    John

    Thursday, March 01, 2012 5:40 PM

All replies

  • What should the script basically do?

    Torsten Meringer | http://www.mssccmfaq.de

    Monday, February 13, 2012 2:19 PM
  • The script:

    1. check for a process and kills it.
    2. check for an application and uninstalls it
    3. checks for files and delete it.

    I would copy the script but the format becomes screwed....


    Monday, February 13, 2012 2:23 PM
  • Has no one got any experiance with deploying powershell scripts?
    Tuesday, February 14, 2012 9:54 AM
  • Usually "it just works" ... you have to find out where and why the powershell script got stuck. You might have to add logging and/or debugging steps to the script in order to get the required information.

    Torsten Meringer | http://www.mssccmfaq.de

    Tuesday, February 14, 2012 10:16 AM
  • Im not using any cmdlts that arnt startard to windows. heres a copy, sorry about the formatting..

    cls



    $computer

    =$Env:COMPUTERNAME


    $app

      =Get-WmiObject-ComputerName$computer-classWin32_Product| Where-Object-FilterScript{$_.Name -match"WorkForceClockIn"}



    if

    ( ( Get-Process| ?{ $_.ProcessName -eq"WorkForceClockIn"}  ) )


    {


    Stop-Process-processname WorkForceClockIn


    }


    Else


    {


    "WorkFoceClockIn NOT running"


    }



     


    if

    (  ( Get-WmiObject-ComputerName$computer-classWin32_Product| Where-Object-FilterScript{$_.Name -match"WorkForceClockIn"}  ) )


    {


    $app.Uninstall()


    }


    else


    {


    "WorksFoceClockIn NOT installed"


    }




    if

    (Get-ChildItem-PathD:\NSB\DOTNET)


    {


    Remove-Item -Path D:\NSB\DOTNET\*


    }


    Tuesday, February 14, 2012 10:34 AM
  • Did you manage to execute PS scripts with sccm

    i have the same isseu

    GPO is configured  Unrestricted still no luck.

    Friday, February 24, 2012 12:59 PM
  • I like to use a wrapper to most of my software distributions to give me a bit more logging.

    I've not tried my wrapper with a powershell script but you could give it a whirl:

    Use it like this:

    Place InstallSoftware.vbs into your package folder

    Create a new file alongside InstallSoftware.vbs called: "Powershell-Test.clist"

    Place your powershell file in the same directory, lets say its called eddiexuk.ps1

    Edit Powershell-Test.clist with your favorite text editor and add the following content:

    powershell.exe -ExecutionPolicy Bypass -NoProfile -File eddiexuk.ps1

    Create a program in the packaged with the following command syntax:

    cscript.exe //Nologo InstallSoftware.vbs Powershell-Test.clist

    If run under system context, it should generate a log file in C:\Windows\AppLog

    'InstallSoftware.vbs Version 2.9.1

    'By Jesse Harris - University of the Sunshine Coast 'Changes from v2.9 ' Bugfix, Environment variables are stored and retreived from Process ' context rather than User context. This bug caused variables to be ' saved into a roaming profile if script was run as a user. 'Changes from v2.8 ' Added variable to working dir path. This is so that you can explicitly ' call executables which may be registered as an App Path in the registry ' Initially implemented for Outlook deployment on XP ' Path to script can be accessed via %SCRIPTPATH% 'Changes from v2.7 ' Added support to run clist files from a UNC path. This allows you double ' click InstallSoftware.vbs or drag and drop a clist file onto ' InstallSoftware.vbs from a UNC path for testing purposes. 'Changes from v2.6 ' Added support for Windows XP, which does not have a Username variable when ' running in the system context. Also Logfile support has a different dir ' when run from user context. Updated for all platforms ' Added feature which allows you to just run the script and it will search ' for Clist files in the current directory. If multiple Clist files are ' found all of the commands in all of the clists will be run. Order of ' running is determined alpha-numerically. ' Added more error reporting and handling for log files. Script will now exit ' more gracefully if the log file cannot be opened for append. 'Changes from v2.5 ' Added special variables for 64 and 32 bit registry entries. See ' instructions for details 'Changes from v2.4 ' Added custom error handling of command execution. ' Previously if a command in the clist was not found ' the execution would end with no feedback in the log. ' Now an error level is generated (Exit code 1) with a message in the Log. 'Changes from v2.3 ' Under System context a new log folder is dedicated to log files ' Folder is %Windir%\AppLog ' Folder will be auto-created on first use. ' Recommended filename convention for commandlist is: ' SoftwareTitle-Action.clist (eg, Sophos-PrepareOS.clist, DotNet4-Install.clist) 'Changes from v2.2 ' Changed logging to include dates of each line written to text file. ' Increased pause between commands to 5 second ' Fixed issue with detecting user/system context 'Changes from v2.1 ' Added builtin dos variables for x86 and x64 independance. See instuctions ' for more details 'Changes from v1.0 ' Added logging of Dos command errors as well as Dos command standard output ' Reworded instructions 'Instructions 'Simple InstallSoftware.VBS file 'Call with "cscript //Nologo InstallSoftware.vbs commandlist 'Where commandlist is a plaintext file with a series of commands to be executed 'commands in the commandlist file must be executable files. 'Dos shell commands such as dir, copy, del are not executable files 'To use these types of commands, prefix them with "cmd /c del file.txt" 'What this script does: '1 Detects whether running under User or System context and writes a logfile ' System logfile is: C:\Windows\Temp ' User logfile is: %temp% '2 Creates Env variables %DEFAULTSYS% and %DEFAULTPROG%, %REGISTRY%, %SCRIPTPATH% ' %DEFAULTSYS% = SYSWOW64 on x64 and SYSTEMP 32 on x86 ' %DEFAULTPROG% = Program Files (x86) on x64 and Program Files on x86 ' %REGISTRY% = "HKLM\Software" on x86 and "HKLM\Software\Wow6432Node" on x64 ' examle: reg add "%Registry%\Adobe\Reader" /v "Version" /d "11" ' %SCRIPTPATH% = \\wsp-sccm01\smspkgd$\USC00001 or ' %SCRIPTPATH% = C:\Windows\Syswow64\CCM\Cache\USC00001 '3 Executes dos commands without spawning a dos window '4 Logs Dos output to log file 'Configure Environment Dim WshShell, strCommand, oExec, Context Dim CMDFile() Set FileSystem = WScript.CreateObject("Scripting.FileSystemObject") Set WshShell = CreateObject("Wscript.Shell") 'Get list of commands to be run from Command List file CMDList = GetCMDList() 'Get location of logfile depending on login conext and set Context variable LogFile = GetLogFile() 'Initiate logfile and display context. LogInfo "Script is running in " & Context & " Context" 'Set Architecture Special Folders ENV Variables and Reg Keys Call SetSYS64() 'Execute Commands LogInfo "Executing CMDList from " & CMDFile(0) For Each CMDListSet in CMDList For Each CMD in CMDListSet Call Install(CMD) Next Next 'Wrap it up LogInfo "All Commands Executed" wscript.quit 'Functions function Install(strCommand) 'Executes each command and redirects stderror and stdout to the logfile On Error Resume Next LogInfo "Executing (" & strCommand & ")" Set oExec = WshShell.Exec(strCommand) If Err Then LogInfo "Error # " & CStr(Err.Number) & " " & Err.Description LogInfo "Exiting with error code 1" wscript.quit 1 End If Do While Not oExec.StdOut.AtEndOfStream sLine = oExec.StdOut.ReadLine LogInfo sLine Loop Do While Not oExec.StdErr.AtEndOfStream sLine = oExec.StdErr.ReadLine LogInfo sLine Loop ' Pause for 5 seconds to allow the installer to finish properly Wscript.Sleep 5000 end function function GetLogFile() Set Shell = WshShell.Environment("Process") UserName = wshShell.ExpandEnvironmentStrings( "%USERNAME%" ) ComputerName = wshShell.ExpandEnvironmentStrings( "%COMPUTERNAME%" ) If ComputerName = Left(UserName,Len(Username)-1) or Username = "SYSTEM" or Username = "%USERNAME%" then LogDir = wshShell.ExpandEnvironmentStrings( "%Windir%" ) & "\AppLog\" If Not FileSystem.FolderExists(LogDir) Then Set NewFolder = FileSystem.CreateFolder(LogDir) End If Context = "System" Else LogDir = wshShell.ExpandEnvironmentStrings( "%TEMP%" ) & "\" Context = "User" End If clistFileName = CMDFile(0) arrPath = Split(clistFileName, "\") intIndex = Ubound(arrPath) LogThing = arrPath(intIndex) GetLogFile = LogDir & LogThing & ".log" End Function function SetSYS64() Set Shell = WshShell.Environment("Process") Arch = wshShell.ExpandEnvironmentStrings( "%PROCESSOR_ARCHITECTURE%" ) If Arch = "AMD64" then Shell( "DEFAULTSYS" ) = "%SystemRoot%\SysWOW64" Shell( "DEFAULTPROG" ) = "%ProgramFiles(x86)%" Shell( "REGISTRY" ) = "HKLM\SOFTWARE\Wow6432Node" else Shell( "DEFAULTSYS" ) = "%SystemRoot%\System32" Shell( "DEFAULTPROG" ) = "%ProgramFiles%" Shell( "REGISTRY" ) = "HKLM\SOFTWARE" End if 'Shell( "SCRIPTPATH" ) = Replace(WScript.ScriptFullName, WScript.ScriptName, "") scriptPath = FileSystem.GetFile(Wscript.ScriptFullName).ParentFolder LogInfo "Working Dir is: " + scriptPath Shell( "SCRIPTPATH" ) = scriptPath End function function GetCMDList() intCount = 0 ReDim Preserve CMDFile(intCount) Set args = WScript.Arguments If args.Count = 0 then 'Find CMD file by yourself objStartFolder = "." Set objFolder = FileSystem.GetFolder(objStartFolder) Set colFiles = objFolder.Files For Each objFile in colFiles If FileSystem.GetExtensionName(objFile) = "clist" Then ReDim Preserve CMDFile(intCount) CMDFile(intCount) = objFile.Name intCount = intCount + 1 End If Next Else CMDFile(0) = args.Item(0) End If If CMDFile(0) = "" then wscript.echo "Error, no CMD file: Exiting with error code 1" 'LogInfo "Error, no CMD file" 'LogInfo "Exiting with error code 1" wscript.quit 1 End If Const ForReading = 1 intCount = 0 For Each ClistFile in CMDFile Set objCMDFile = Filesystem.OpenTextFile(ClistFile, ForReading) ReDim Preserve Clist(intCount) Clist(intCount) = Split(objCMDFile.ReadAll(), VbCrLf) intCount = intCount + 1 Next GetCMDList = Clist End Function Sub LogInfo(msg) On Error Resume Next Const ForAppending = 8 wscript.echo msg Set WriteOut = FileSystem.OpenTextFile(LogFile, ForAppending, True) If Err Then wscript.echo "Error # " & CStr(Err.Number) & " " & Err.Description wscript.echo "Error opening log file for appending" wscript.echo "Logfile path: " & LogFile End If WriteOut.WriteLine Date & " " & Time & " " & msg WriteOut.Close Set WriteOut = Nothing End Sub


    Thursday, March 01, 2012 11:09 AM
  • No ive not been able to get this working. I think its the way the powershell script executes in SCCM, a microsft case need to be raised about this. But i carnt afford to burn up any hours on this...

    I ended up putting the intelegents of the script into the installshield program i was creating.

    But for futre regerance it would be extemly usefull for someone to come up with a solution.

    Thursday, March 01, 2012 3:31 PM
  • Hi,

    We powershell scripts for nearly every SCCM software deployment. Every app command line looks like ....

    "powershell.exe -command .\install.ps1"

    We have a internal CA, sign all our code and have those code signing certs pushed out via AD and in our image so are station builds can use our ps1 files.

    The only issue you might incounter which i think is a standard SCCM "thing" - make sure you aren't running any code with "write-host"

    A sample Office install ...

    $Path = "c:\bin\" $ProgessLog = "Office14Install.txt" Function Log-Message(){ Param($Message = ".") Write-Verbose $Message Write-Output $Message | Out-File "$Path$ProgessLog" -Append -Force } $Source = $pwd If (Test-Path "C:\Program Files (x86)"){ $PF="C:\Program Files (x86)" } Else { $PF="C:\Program Files" } Log-Message "Office14 Installation - The Present Working Directory is $pwd" $Products = @(get-wmiobject -class "Win32_Product") $OfficeFlag = @($Products | Where-Object {$_.Name -like "Microsoft Office Professional Plus 2010"}) If ($OfficeFlag){ $cmd = 'cmd /C ' + $Source + '\source\setup.exe /repair ProPlus /config ' + $Source + '\source\ProPlus.WW\SilentRepairConfig.xml' } Else{ $cmd = 'cmd /C ' + $Source + '\source\setup.exe' } Log-Message "Office14 Installation cmd is set to $cmd" Invoke-Expression $cmd | Out-Null Log-Message "Office14 Installation - Command execution finshed - Exit Code: $LASTEXITCODE" Remove-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "BCSSync" -Force

    Do you use SCCM Client Control Center? I find the execution history is really useful for finding out why an app has failed to install.

    Fingers crossed it works better for you a second time.

    Thanks,

    John

    Thursday, March 01, 2012 5:40 PM
  • John,

    Can you clarify 'The only issue you might incounter which i think is a standard SCCM "thing" - make sure you aren't running any code with "write-host"'

    Are you stating that you cannot use this command when deploying a ps1 script via SCCM? 

    Cheers

    Saturday, April 28, 2012 11:11 PM
  • Hi,

    I was told that if you right a script with outputs to the console (write-host) that the code would fail. I think this is similar to when you deploy Office (http://technet.microsoft.com/en-us/library/ff404178.aspx) ,when you're creating the custom install you have to se the display level to none (not just progress only).

    Thanks,

    John

    Monday, April 30, 2012 9:08 AM
  • Hi John,

    Your correct. I fount the issue after MDT stopped running my scripts remotly with execution policy set to "remote signed". Some may see its always been the case but im sure a patch has been released that now stops unsigned scripts running under this execution policy.

    I need to look into signing scripts.. which is tedious!

    Just a squick question john, do you sign all your scripts with the same CA and just rollout one cert through GPO?

    Monday, April 30, 2012 10:56 AM
  • Both :o)

    My initial understand was that if we had a trusted CA sign a code signing cert that the system would trust it would implicitly but that didn’t work for us (I could be wrong it might work for you)

    We use our own internal CA but found we also had to add each “code writers” cert via GPO.

    We’ve got over 400 scripts now … I’m not looking forward to having to resign them! I should be quite simple to resign them as an automated process but poor old SCCM will have to update all the DPs!

    Monday, April 30, 2012 11:30 AM
  • John, are you saying that you have to sign your Powershell scripts in order for SCCM to run them? 
    Friday, May 10, 2013 8:03 PM
  • Hi,

    You don't HAVE to  .... but in my enviroment we have the Powershell execution policy set to ALLSIGNED. You could set the plicy to "Unrestricted" but IMO that would be bad security practice.

    J

    Monday, May 13, 2013 8:31 AM
  • In our environment, we ran into the same issues described above. We are taking strides to get into position to make powershell our "script machine" of choice. While sorting it out, since you are obviously not going to be able to deploy a CA as quickly as you need the scripts, set a GPO to "remotesigned" or "allsigned". This should fix the issue in the interim while you get your CA up and running. I now use powershell scripts for all of my scripting needs that cannot be simply run through command line. I like to deploy software broken down into the most granular elements. This allows me to see exactly what is happening...that's just my methodology...of course there are those who would say it is better to run "all-in-one" packages. At the end of the day...if it works, it works:)
    Tuesday, October 08, 2013 11:46 AM
  • -ExecutionPolicy parameter to set the ExecutionPolicy value for that session.  Example:

    powershell.exe -ExecutionPolicy Unrestricted -File script.ps1

    Tuesday, March 25, 2014 5:23 PM