Saturday, 31 March 2018

SCCM Device Collection Details with No deployment

How to get SCCM collection names where No deployments are targetted?

Sometimes we may need to clear all the unwanted collection as part of our SCCM maintenance activity. Usually, we used to delete the device collection from the Assets and Compliance workspace, select Device Collections, check the collection members and it's targetted deployments, right click on the collection and delete. This task will take a lot of time if we have a huge number of collections in our organization, So I thought of to automate this process using PowerShell script.I have also included the collection's member count which will be helpful in case if we need to delete the collection based on the collection's member count

Please follow the below PowerShell script to check SCCM device collections with No deployment and delete it if the collection is no longer required.

We need to modify the report path in the 1st line of this script, highlighted in yellow

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$reportpath = "K:\OSDLogs\Result.csv" # Provide CSV path with CSV Name

Import-module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + '\ConfigurationManager.psd1')
$SiteCode = Get-PSDrive -PSProvider CMSITE
Set-location $SiteCode":"
$details = @()
$CollectionList = Get-CmCollection | Where-Object {$_.CollectionID -notlike 'SMS*' -and $_.CollectionType -eq '2'} | Select-Object -Property Name,MemberCount,CollectionID,IsReferenceCollection

foreach ($Collection in $CollectionList)
{
    $NumCollectionMembers = $Collection.MemberCount
    $CollectionID = $Collection.CollectionID
    $GetDeployment = Get-CMDeployment | Where-Object {$_.CollectionID -eq $Collection.CollectionID}  
    if ($GetDeployment -eq $null)
    {
        $collectionName = $Collection.Name
        $Membercount = $Collection.MemberCount
        $Result = [ordered]@{
        Collection_NAME     = "$collectionName"
        Member_Count     = "$Membercount" }
        $Details += New-Object PSObject -Property $Result
    }
}

The output of this script will be as shown below - 












As per the above script, we are not deleting the collection which doesn't have any deployments targetted to it. If we wanted to delete the collections with no deployments targetted, we need to modify the script under "IF" loop as shown below. 

Please note: The collection will be deleted forcefully without no notifications as per the below script, you can make the modification if required

1
2
3
4
5
6
7
        Remove-CMCollection -Id $CollectionID -Force
        $collectionName = $Collection.Name
        $Membercount = $Collection.MemberCount
        $Result = [ordered]@{
        Collection_NAME     = "$collectionName"
        MemberCount     = "$Membercount" }
        $Details += New-Object PSObject -Property $Result

Click here to download the script

Sunday, 19 February 2017

Data replication using PowerShell Script

Data replication using PowerShell Script + Create log files for your scripts

The below article is focused on data replication from one source to destination - Not a folder synchronization script. It's always been a question to me how to replicate the content of one folder to another for a various purpose. This article also included logging option in Powershell, So we can follow, and see what is happening while the script is executing and copying the data or if the script runs into errors is very useful. Please follow the below article to know how can we achieve this using PowerShell.

Below cmdlets would be the input for the script. We need to define the source location, destination location and log file path for the script.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
###################################
# Source Location Details
#-------------------
$Source = "\\Server2012\ContentFolder"

# Destination Details
#-------------------
$Destination = "D:\ContentFolder"

# Log File Location
#-------------------
$Folderpath = "D:\FolderSync"

We have to declare few variable such as Log file name, error handling variables etc.

1
2
3
4
5
6
7
8
9
####################################
# Declaration of Variables
$Date1 = Get-Date -UFormat "%m-%d-%y"
$time = Get-Date -Format g
$Logname = "SyncLog.log"
$FilePath = "$Folderpath"+ "\$Logname"
$Errormap = $null
$errorcheck  = ''
$error.Clear()

The next part will be Create\Modify the Log file based on the availability of log and based on the size of the log - If the log file size is more than 1 MB (We can modify the size) log file will be replaced with a new log file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
###################################

# Setting Log File

    If ((Test-Path -Path $Folderpath))
    {
            if ((Test-Path -Path $FilePath))
            {
 
                $filesize = Get-ChildItem $FilePath ; if ($filesize.Length -gt 100kb ) {Remove-Item $filesize.FullName -Force; New-Item $logfolder\$Logname -ItemType file -Force} 
                
                else {}
             }
            else
            {
                New-Item $FilePath -ItemType file -Force
            }
    }
    else
    {

        New-Item $Folderpath -Type directory -Force
        New-Item $FilePath -ItemType file -Force

    }

Checking the source location is accessible or not from the destination location - The script execution will exit in case if the source not accessible.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Connection to Source

If ((Test-Path -Path $Source))
    {
        Add-Content -Path $FilePath -Value "$time Connection to $Source established Successfully`n"  
    }
else
    {
        Add-Content -Path $FilePath -Value "$time Error : Connection to $Source Failed - Script execution stopped`n"
        exit
    }

The System.Security.Cryptography namespace provides cryptographic services, including secure encoding and decoding of data, as well as many other operations, such as hashing, random number generation, and message authentication. For more information. Refer the below link for more details.

https://msdn.microsoft.com/en-us/library/system.security.cryptography(v=vs.110).aspx

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
##################################
function Get-FileMD5 {
    $error.Clear()
    Param([string]$file)
    $mode = [System.IO.FileMode]("open")
    $access = [System.IO.FileAccess]("Read")
    $md5 = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
    $fs = New-Object System.IO.FileStream($file,$mode,$access)
    $Hash = $md5.ComputeHash($fs)
    $fs.Close()
    [string]$Hash = $Hash
    Return $Hash
}
Add-Content -Path $FilePath -Value "$time Data Replication Started`n"

Below part is replicating data and writing the respective logs into log file


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
##################################

function Copy-LatestFile{
    $error.Clear()
    Param($File1,$File2,[switch]$whatif)
    $File1Date = get-Item $File1 | foreach-Object{$_.LastWriteTimeUTC}
    $File2Date = get-Item $File2 | foreach-Object{$_.LastWriteTimeUTC}
    if($File1Date -gt $File2Date)
    {
        #Write-Host "$File1 is Newer... Copying..." -ForegroundColor Green
        Add-Content -Path $FilePath -Value "$time $File1 is Newer. Copying...`n"
        if($whatif){Copy-Item -path $File1 -dest $File2 -force -whatif}
        else{Copy-Item -path $File1 -dest $File2 -force}
        $errormap = $error[0]
        if($error[0] -eq $null) {} else {Add-Content -Path $FilePath -Value "$time Error $errormap"; $errorcheck = "1"}
    }

}

# Getting Files/Folders from Source and Destination
$SrcEntries = Get-ChildItem $Source -Recurse
$DesEntries = Get-ChildItem $Destination -Recurse

# Parsing the folders and Files from Collections
$Srcfolders = $SrcEntries | Where-Object{$_.PSIsContainer}
$SrcFiles = $SrcEntries | Where-Object{!$_.PSIsContainer}
$Desfolders = $DesEntries | Where-Object{$_.PSIsContainer}
$DesFiles = $DesEntries | Where-Object{!$_.PSIsContainer}

foreach($folder in $Srcfolders)
{
    $error.Clear()
    $SrcFolderPath = $source -replace "\\","\\" -replace "\:","\:"
    $DesFolder = $folder.Fullname -replace $SrcFolderPath,$Destination
    if(!(test-path $DesFolder))
    {
        Add-Content -Path $FilePath -Value "$time Folder $DesFolder Missing. Creating it!`n"
        new-Item $DesFolder -type Directory | out-Null
        $errormap = $error[0]
        if($error[0] -eq $null) {} else {Add-Content -Path $FilePath -Value "$time Error  $errormap"; $errorcheck = "1"}
    }
}

foreach($entry in $SrcFiles)
{
    $error.Clear()
    $SrcFullname = $entry.fullname
    $SrcName = $entry.Name
    $SrcFilePath = $Source -replace "\\","\\" -replace "\:","\:"
    $DesFile = $SrcFullname -replace $SrcFilePath,$Destination
    if(test-Path $Desfile)
    {
        $SrcMD5 = Get-FileMD5 $SrcFullname
        $DesMD5 = Get-FileMD5 $DesFile
        If(Compare-Object $srcMD5 $desMD5)
        {

            Add-Content -Path $FilePath -Value "$time The Files MD5's are Different... Checking Write`n"
            Copy-LatestFile $SrcFullname $DesFile
            $errormap = $error[0]
            if($error[0] -eq $null) {} else {Add-Content -Path $FilePath -Value "$time Error  $errormap"; $errorcheck = "1"}
        }
    }
    else
    {
        $error.Clear()
        Add-Content -Path $FilePath -Value "$time $Desfile Missing... Copying from $SrcFullname`n"
        copy-Item -path $SrcFullName -dest $DesFile -force
        $errormap = $error[0]
        if($errormap -eq $null) {} else {Add-Content -Path $FilePath -Value "$time Error  $errormap"; $errorcheck = "1"}
    }
}

# Checking for Files that are in the Destinatino, but not in Source
foreach($entry in $DesFiles)
{
    $error.Clear()
    $DesFullname = $entry.fullname
    $DesName = $entry.Name
    $DesFilePath = $Destination -replace "\\","\\" -replace "\:","\:"
    $SrcFile = $DesFullname -replace $DesFilePath,$Source
    if(!(Test-Path $SrcFile))
    {

        Add-Content -Path $FilePath -Value "$time $SrcFile is deleted from Source... Deleting it from Destination`n"
        Remove-Item -path $DesFullname -Recurse -force
        $errormap = $error[0]
        if($error[0] -eq $null) {} else {Add-Content -Path $FilePath -Value "$time Error  $errormap"; $errorcheck = "1"}
    }
}
$error.Clear()
$Source = Get-ChildItem $Destination -Recurse
$dirs = $Source | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName
$dirs | Foreach-Object { Remove-Item $_ }
if ($errorcheck -ne '1') { Add-Content -Path $FilePath -Value "$time Data Replication completed successfully`n"}
else {Add-Content -Path $FilePath -Value "$time Data Replication completed with some error - Please check the log for details`n"}

###################################

Combine all the script together and run.

Click here to download the script from Technet

Tuesday, 16 August 2016

File Search - Using Powershell

How to search a file located in Local\Remote System using PowerShell

As you can see,  this is about a simple WMI query + Powershell for searching a file with the file extension. In this script we are connecting to the wmi service of a Local\Remote computer and executing the query to get the file details. After getting the details we setup a foreach loop for the collection to extract the details.

Below is the two important WMI query which we are executing through powershell. I am searching a files with bmp extension.

1
2
$SearchObject = Get-Wmiobject -namespace "root\CIMV2" -computername $System -Query "Select * from CIM_DataFile Where Extension = 'bmp'"
$query = "ASSOCIATORS OF {Win32_LogicalFileSecuritySetting='" + $filepath + "'} WHERE AssocClass=Win32_LogicalFileOwner ResultRole=Owner"

Note : As per the below script, we are running the query against entire file system which will take long time to get the details based on the number of files present on the targeted machine. Also you can search multiple extension as well by adding AND or OR  operator.

Below is the information which we are additionally getting from the script.
1
2
3
4
5
6
7
8
MACHINE_NAME
PING_STATUS
FILE_PATH
USER_NAME
FILE_SIZE
LAST_MODIFIED
SYSTEM_MAKE
SYSTEM_MODEL

Below is script which is created based on a  project requirement. In this script I have copied the system hostnames in a .TXT file for searching from multiple system. ( This is required only if you are searching the files in multiple systems.)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
$details = @()
$PCList = Get-Content C:\temp\PC.TXT

foreach ($System in $PCList)
{
$pstsize = ""
$modified = ""
$Make = ""
$model = ""
$SearchObject = $null

If(!(Test-Connection -Cn $System -BufferSize 16 -Count 1 -ea 0 -quiet))
{
    $Result = @{
    MACHINE_NAME     = "$System"
    PING_STATUS      = "FAILED"
    FILE_PATH        = "N/A"
    USER_NAME        = "N/A"
    FILE_SIZE         = "N/A"
    LAST_MODIFIED    = "N/A"
    SYSTEM_MAKE = "N/A"
    SYSTEM_MODEL = "N/A"
                        }
    $Details += New-Object PSObject -Property $Result
}
Else
{

    $MakeDetails = Get-WmiObject -Class win32_computersystem -ComputerName $System
    $Make = $MakeDetails.Manufacturer
    $Model = $MakeDetails.Model
      
    $SearchObject = Get-Wmiobject -namespace "root\CIMV2" -computername $System -Query "Select * from CIM_DataFile Where Extension = 'bmp'"
    #$SearchObject = Get-WmiObject CIM_Datafile -ComputerName $System | Where-Object {$_.Extension -eq 'txt'}

if($SearchObject)
{

    foreach ($ObjectFile in $SearchObject)
    {

        $filepath = $ObjectFile.Drive + $ObjectFile.Path + $ObjectFile.FileName + "." + $ObjectFile.Extension
        $query = "ASSOCIATORS OF {Win32_LogicalFileSecuritySetting='" + $filepath + "'} WHERE AssocClass=Win32_LogicalFileOwner ResultRole=Owner"
        $FileOwner = Get-Wmiobject -namespace "root\CIMV2" -computername $System -Query $query

        $FileOwnerName = $FileOwner.AccountName

        $output = $System + "," + $filepath + "," + $filepath + "," + $ObjectFile.FileSize/1KB + "," + $ObjectFile.LastModified
        $modified = $ObjectFile.LastModified
        $pstsize  =    $ObjectFile.FileSize/1KB

        $Result = @{
        MACHINE_NAME     = "$System"
        PING_STATUS      = "SUCCESS"
        FILE_PATH        = "$filepath"
        USER_NAME        = "$FileOwnerName"
        FILE_SIZE         = "$pstsize"
        LAST_MODIFIED    = "$modified"
        SYSTEM_MAKE      = "$Make"
        SYSTEM_MODEL     = "$model"
                            }
        $Details += New-Object PSObject -Property $Result
        }
    }
    else
    {    
        $Result = @{
        MACHINE_NAME     = "$System"
        PING_STATUS      = "SUCCESS"
        FILE_PATH        = "NO PST FILE FOUND"
        USER_NAME        = "N/A"
        FILE_SIZE         = "N/A"
        LAST_MODIFIED    = "N/A"
        SYSTEM_MAKE = "$Make"
        SYSTEM_MODEL = "$model"
                            }
        $Details += New-Object PSObject -Property $Result
    }
}
}

$pathofcsv = "C:\TEMP\" + "FILE_DETAILS" + "$date" + ".csv"
$Details | export-csv -Path $pathofcsv -Append -NoTypeInformation

The output of the script will be as given below.



Saturday, 6 August 2016

WINDOWS 10 IN-PLACE UPGRADE

WINDOWS 10 IN-PLACE UPGRADE USING SYSTEM CENTER CONFIGURATION MANAGER 2012 R2-SP1

1.1 Prerequisites

·         A System Center 2012 R2 Configuration Manager site is already installed and configured.
·         Configuration Manager Clients are deployed and running at least Windows 7 OS.
·         Windows 10 Enterprise (32 and 64 Bit) media (ISO file should be extracted as pictured below).

·         








Download the Task sequence from the below URL and extract it to a network share.
It should look like below.
Windows-vNextUpgradeExport.zip
Windows-vNextUpgradeExport_files
o    Windows vNext Upgrade Media (empty)
o    Windows vNext Upgrade Script
https://blogs.technet.microsoft.com/enterprisemobility/2015/06/16/revised-content-for-the-windows-10-in-place-upgrade-via-task-sequence-for-configuration-manager/

1.2 Preparation of Task Sequence.

Extract the downloaded Task Sequence to our deployment root (Where we are placing all the software binaries required for SCCM)
1.      Copy the contents of the Windows 10 Enterprise media into the “Windows vNext Upgrade Media” subfolder. An Upgrade package automatically creates during the Import TS process explained below (Point-3).










2.    “Windows vNext Upgrade Scripts” folder contains the required PowerShell script during the Upgrade process. An Upgrade Script Package automatically creates during the Import TS process.






3.      Open the Configuration Manager Console, switch to the Software Library workspace, expand Operating Systems, right-click Task Sequences, and select Import Task Sequence
Note:
Downloaded Task Sequence cannot be imported twice on our SCCM console. We have to Right Click on the Imported Task Sequence and Copy to have another Task Sequence – We may need two TS for importing multiple Built/Version for Windows10 Operating System. Process is explained in the below article.

1.3 Task Sequence Creation

1)  Open the Configuration Manager Console, switch to the Software Library workspace, expand Operating Systems, right-click Task Sequences, and select Import Task Sequence











2)    Browse or Enter the UNC path of vNextUpgradeExport.zip










3) Select Windows-vNextUpgradeExport.zip and click Next











4)  The File Content page will display the following objects with the action, Create New (Action will be Ignore Duplicate in case if we are tried to Import the same TS again.)

o   Windows vNext Upgrade Media (Package)
o   Windows vNext Upgrade Scripts (Package)
o   Windows vNext Upgrade (Task Sequence Package)













5)    Click on close button from summary page.











6)    Windows10 Upgrade Task Sequence is ready for the deployment. Distribute the Task Sequence to appropriate distribution point – Below is the reference package which is related to the Task sequence.

o   Windows vNext Upgrade Media
o   Windows vNext Upgrade Scripts

7)    Make sure that these two packages are distributed properly before starting Upgrade.












1.4 Task Sequence Modification.

Below are the Important steps involved in the task sequence, we can Right Click on the Task sequence to edit it.

Check Readiness:

These are the minimum system requirements for Windows but can be adjusted as necessary for your environment. Windows Upgradation will fail on Check Reediness step in case the systems are not met as per the configuration.

1.                  Minimum Memory : 2048 in MB
2.                  Minimum Processor : 1024 in MHz
3.                  Free Disk space in System Directory : 5120 in MB
4.                  Current OS to be referred is : CLIENT (Client Operating Systems)














Note:

SMSTS.Log will provide all the information about Task Sequence and we can refer this log for troubleshooting purpose.

PreSetup:  This runs a corresponding Windows PowerShell script (PreSetup.ps1) to perform a variety of necessary actions prior to running Windows Setup.

Stage Content:  A simple copy of some scripts to a known, local staging directory to be referenced elsewhere in the process

Upgrade Windows:

This is where setup is actually run from the media package to automatically upgrade Windows.

As mentioned earlier in this article, downloaded Task Sequence cannot be imported twice on our SCCM console. We have to follow the below steps for creating another Task sequence from the existing one.
           
1.      Create a Package without program using Windows-10 source file and distribute the package to required distribution point.
2.      Create a New Copy of Task Sequence from our Imported Task sequence (Right Click on the TS and Copy it).
3.      Edit the New Task Sequence and click on Upgrade Windows Step.
4.      Click on the Browse Button and point the newly created Windows-10 Package (Highlighted in red on the above picture)
















Restart:  The task sequence is configured such that this step is only processed if the Upgrade Windows step succeeds. These three steps (Upgrade Windows Recover from Setup Failure, and Restart Computer) need to be kept together for the process to work properly.

·     We can Modify the User Notification on Restart Window
·     Message Delay can be increased as per our requirement. Default time-out will be 30 second.

Post-Processing:   This group is processed when setup successfully completes. Additional steps can be added to this group as needed, such as Install Applications, Run PowerShell and Run Command Line etc.

Rollback: This group is only processed in a rollback scenario. Additional steps can be added to this group as needed, for example to copy logs to a network share. The Trigger Rollback Failure step should be the last one in that group to purposefully fail the task sequence for proper status monitoring.