Showing posts with label Powershell. Show all posts
Showing posts with label Powershell. Show all posts

Tuesday, May 5, 2015

Find SCCM Device Variables Using Powershell

Sometimes you want a report of all the variables that computer/device will have when it runs the task sequence.

Here's a script that will report ...
a) device name
b) device assigned variables
c) Collection name with the assigned variables of which the device is a member.

It requires the SCCM console installed and you must provide the computer name in the command line.

The script will report back if the computer name is not valid and kill the powershell process.
For testing, run the script from a command prompt. i.e. powershell .\variables.ps1 computername

param (
    [Parameter(Mandatory=$true, HelpMessage="Computer Name",ValueFromPipeline=$true)] $strComputer
)
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
cls
import-module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + '\ConfigurationManager.psd1')
CD SITENAME:\
$nl = [Environment]::NewLine
$tab = "`t" #tab
$count = -1 # Allows for Null
write-host Checking Computer Name
If(!$strComputer){
    [System.Windows.Forms.MessageBox]::Show("Error! - No Source Computer Entered")
    stop-process $pid -Force #End Powershell task
}
#Verify Names
$Verify =  Get-CMDevice -Name $strComputer | forEach{$_.name}
If(!$Verify){
    [System.Windows.Forms.MessageBox]::Show("No Record for "+ $strComputer)
    stop-process $pid -Force #End Powershell task
}
#Get-CMDeviceVariable -DeviceName <String>
# Find System Variables
write-host Checking System Variables
$Result = Get-CMDeviceVariable -DeviceName $strComputer
If(!$Result){
    #write-host $strComputer Not Found
    $strOutput = $strComputer
}
else{
    $Result = Get-CMDeviceVariable -DeviceName $strComputer | forEach{$_.Name + $tab + $_.Value} |Out-String
    $strOutput = $strComputer.toUpper() + " Assigned Device Variables"+ $nl + "Variable Name" +$tab +"Variable Value" +$nl + $Result
}
If($ErrCount -ge 1){
    [System.Windows.Forms.MessageBox]::Show("Error" + $ErrCount)
    stop-process $pid -Force #End Powershell task
}
$strOutput = $strOutput + $nl + $strComputer.toUpper() + " Assigned Collection Variables"
write-host Checking Collection Variables
#Find Collection Variables
$ResID = Get-CMDevice -Name $strComputer | forEach{$_.ResourceID}
$Collections = (Get-WmiObject -ComputerName "SCCM SERVER NAME" -Class sms_fullcollectionmembership -Namespace root\sms\site_SITENAME -Filter "ResourceID = '$($ResID)'").CollectionID
foreach($collection in $Collections){
    $CollName = Get-CMDeviceCollection -CollectionId $Collection | forEach{$_.name}
    write-host $CollName
    $CollVar = Get-CMDeviceCollectionVariable -CollectionName $CollName | forEach{$CollName + $tab + $_.Name + $tab + $_.Value} |Out-String
    If ($CollVar){
        $CollOutput = "Collection Name" +$tab + "Variable Name" + $tab + "Variable Value"
        $CollOutput = $CollOutPut + $nl + $CollVar
    }
}
$strOutput = $strOutput + $nl + $CollOutput
#[System.Windows.Forms.MessageBox]::Show($strOutput)
[IO.File]::WriteAllText('variables.txt', $strOutput)



More SCCM related posts

Monday, April 20, 2015

Blocking Cyber Attacks on Windows Servers using CyberArms software and PowerShell scripts

Recently the number of audit failures on our Remote Desktop and IIS servers has increased drastically in the last month.
List of IP addresses found here.
Found this program for our 2012 servers (or any RDP using TLS)

CyberArms Intrusion Detection and Defence System

Default config...
After 3 failed login attempts, it would temporarily block the IP for 20 minutes. (Soft Lock)
After 10 login attempts, it would block the IP for 24 hours. (Hard lock)
-There is the option to hard lock the IP address forever.

Issues
1) No option to customized notification emails. -wanted to include
2) Cannot white-list a service provider only IP addresses.
3) No Response to inquiry about their products and licensing.

Since the program creates an event log for when it blocks the IP address, a PowerShell script was created to add firewall rules (permanent rules) and send email notifications out with detailed information.

The original PowerShell script just sent out emails with the server name when an event log with 4001 or 9001 is created in the "CyberArms" event log.

It worked great on our 2012 servers, however 2008 stored the Event log data with extra returns and spaces in the IP addresses.  In testing it caused the local network to be blocked via firewall.  Good thing it was a virtual machine and could still be access through Virtual Center.

Updated the PowerShell script to distinguish between 2008 and 2012 servers.

Another VBscript was created to manually delete any firewalls from 'trusted' IP addresses using netsh from another computer.

After the first weekend having to check each IP address and manually delete the rules got old and time consuming.

The next revision of the script added NSlookup to a ProviderLookup function in the script.

The ProviderLookup function was created to allow certain IP addresses or provider names (provided by NSLookup) to be unblocked automatically.

Example if NSlookup returned a provider name with "Domain.ca" in it, then we trusted it and the firewall was deleted.

Emails now include the IP address, the provider information and if the rule was deleted.

Steps...
Install CyberArms software
Enable any security agents in CyberArms
Download PowerShell Script "CyberArms Eventlog Script.txt"
- Rename to .PS1 and change "DOMAIN.CA" and email settings.
Create Task Sequence to run the script when event 4001 or 9001 is logged.

Update - April 21, 2015

The ProviderLookup function will now include Country and ISP information extracted from http://ipaddress.com if it cannot find a DNS name for the IP address.

Future options
1) Create the same firewall rule on more that one server via PowerShell Array variable.
2) Create a pause between rule creation and rule deletion, just in case there was malicious intentions.
- probably use a ping command to 127.0.0.1 for 10 minutes then have it delete the rule.
3) Working on cleaning up the ProviderLookup function so that certain ISP names will be allowed.

Thursday, April 2, 2015

Removing a Lync Contact using Powershell and XML


Back in February I posted about adding contacts to Lync Groups.
However, what happens when you spell the contact address wrong and you just imported that into your 1000+ clients contact list...

Well, you need to find a way to remove the contact from the XML file.
I could have restored the backup files to the user accounts... but didn't think of that until I got this solution working. *

After trying to create an XML script tath use the same script in the previous post, but this time we will replace the offending string with blanks.

Add this to the Configurable Variables section
#Delete this line from XML
$Variable = @'
<Contact Buddy="contact@domain.com" SubscribePresence="1" Groups="1 20" />
'@
$CommonContact = "contact@domain.com" # The one that is spelled wrong.

In the Process-Contacts Function,

Change If ($ContactsToAdd) to If ($ContactsToAdd) 

Remove the $ContactsToAdd section and replace it with.

$DeleteRecord = (Get-Content "$WorkingFolder\DocItemSet.xml") |
    ForEach-Object {$_ -replace $Variable, ""} |
    Set-Content "$WorkingFolder\DocItemSet.xml"

* This does remove the contact from the user's visual list of contacts.  However, when the user's contact info is exported, the old contact is still there just not listed in any groups which is why it doesn't appear in the client.

In short, double check spelling and try it on a test account.

To change the script from all users to 1 users change...
$AllSipAddresses = Get-CsUser -Identity lyncroom@domain.com -Filter {Enabled -eq $True} | Select SipAddress | %{$_.SipAddress.Replace('sip:','')}

Original Post about adding groups found here on Roland Paix's IT Blog

More on the custom Lync rooms found here.

Friday, February 20, 2015

Bulk Import Custom Contact Groups into Lync User accounts

Was looking for a way to Populate our Lync Room systems into end users Lync Contacts as a group rather than a bunch of individual accounts.  (More on Lync Room Systems here http://bit.ly/netadmlr)

Based on the PowerShell script found below, a new section was created for the Contact Groups.
http://rp-it.blogspot.ca/2014/02/automatically-populate-contacts-for-all-lync-2013-users.html

Rather than repost the entire code, here's the updated sections...
For each Lync group that you want to create I would suggest creating new scripts.

For Individual account testing...

#Old version Enable for All users Disable for individual testing
$AllSipAddresses = Get-CsUser -Filter {Enabled -eq $True} | Select SipAddress | %{$_.SipAddress.Replace('sip:','')}

#GM Added - Enable for individual testing
#$AllSipAddresses = Get-CsUser -Identity lastname.first@domain.com -Filter {Enabled -eq $True} | Select SipAddress | %{$_.SipAddress.Replace('sip:','')}


# Common Contacts - Enable and populate this variable with a set of Lync contacts to distribute to all users instead of distributing all users to all users. Overrides $DontAddUsers.
#$CommonContacts = "lrs1@domain.com","lrs2@domain.com"

To add /verify the Group exists.
$AllContacts = @($SipAddressesToAdd | ?{$_ -ne $User})
    [xml]$XMLFile = Get-Content "$WorkingFolder\DocItemSet.xml"
    $XMLPart = $XMLFile.DocItemSet.DocItem | ?{$_.Name -like "urn:lcd:*"}
    $XMLNode = $XMLPart.Data.HomedResource.Contacts
$XMLNodeGroup = $XMLPart.Data.HomedResource.ContactGroups #GM
    $ExistingContacts = @($XMLNode.Contact | Select -ExpandProperty Buddy)
$ExistingContactGroups = @($XMLNodeGroup.ContactGroup | Select -ExpandProperty DisplayName) #GM
#GM Lync Rooms Contact Group Add
If($ExistingContactGroups){
If($ExistingContactGroups -eq "THluYyBSb29tcw=="){
write-host "`r`nLync Room Group Exists. Nothing changed.`r`n"
}
Else{
write-host "`r`nLync Room Group will be added.`r`n"
[System.XML.XMLLinkedNode]$NewContactGroup = $XMLFile.CreateElement('ContactGroup','http://schemas.microsoft.com/RtcServer/2002/11/dbimpexp')
            $NewContactGroup.SetAttribute('Number','20') #High number so as to not overwrite existing groups.
            $NewContactGroup.SetAttribute('DisplayName','CodeFromXML')
            #Display name in ContactGroups found in XMl file in test account
            #Export-CsUserData -PoolFqdn "lyncserver.domain.com" -UserFilter name@domain.com -FileName "c:\scripts\test.zip"
            $XMLNodeGroup.AppendChild($NewContactGroup)
        }
}
Else{
write-host "`r`nNo existing contact groups found.`r`n"
write-host "`r`nAdding New Contact Group XML Element.`r`n"
[System.XML.XMLLinkedNode]$NewNodeGroup = $XMLFile.CreateElement('ContactGroups','http://schemas.microsoft.com/RtcServer/2002/11/dbimpexp')
            [System.XML.XMLLinkedNode]$NewContactGroup = $XMLFile.CreateElement('ContactGroup','http://schemas.microsoft.com/RtcServer/2002/11/dbimpexp')
            $NewContactGroup.SetAttribute('Number','20') #High number so as to not overwrite existing groups.
            $NewContactGroup.SetAttribute('DisplayName','CodeFromXML')
            $NewNodeGroup.AppendChild($NewContactGroup)
            $XMLCGroup = $XMLPart.Data.HomedResource.Containers
            $XMLPart.Data.HomedResource.InsertBefore($NewNodeGroup,$XMLCGroup)
            $XMLFile.Save("$WorkingFolder\DocItemSet.xml")
            [xml]$XMLFile = Get-Content "$WorkingFolder\DocItemSet.xml"
            $XMLPart = $XMLFile.DocItemSet.DocItem | ?{$_.Name -like "urn:lcd:*"}
            $XMLNode = $XMLPart.Data.HomedResource.Contacts
}
#Back to original code
    Write-Host "`r`nExisting contacts:`r`n" 

Change these lines to include 1 and the group number in the $ContactstoAdd | foreach{} section
$NewContact.SetAttribute('Groups','1 20') 

If you have a large organization, you will need to create a new policy or edit the Global Presence policy.  Once all the Lync users had the Lync Rooms in the list, the "Maximum Followers Reached" error showed up on the Lync Room systems.

A new Presence Policy was created and applied to the Lync Room accounts. The MaxCategorySubcriptions was to 3000 and the error went away.

 Since it was only applied to the Lync Room systems it should not drastically increase the server traffic as compared to applying to all our Lync users.

Monday, February 2, 2015

SCCM 2012 Hard Drive with Bad Blocks Baseline


This PowerShell script will detect systems where the main hard drive is developing bad blocks.
Or as I call it, the "Click of Death".

Continuing with my previous posts on SCCM 2012 baselines items...
http://gmnetadmin.blogspot.ca/2015/01/using-sccm-2012-compliance-settings-to.html

Same steps as Option 2 in the above mentioned post but using the discovery script below...
Changes...
  1. You can add more If statements or expand it use ElseIf to record bad blocks from other hard drives.
  2. Or change the text in the If statement to just '*bad block*' to count all bad blocks event entries.


#Version 1.00 Feb 2, 2015
#SCCM Baseline for Bad Block on C Drive
$AllEvents = 0

#Default Event Logs
$Provider = "Disk"
$HourChange = 24 
$TimeNow = [DateTime]::Now
$TimeChange = [DateTime]::Now.AddHours(-$HourChange)

#Check Event Log for errors
$EventLogNames = "System"

ForEach ($log in $EventLogNames){
    $entry = " " 
    #Search Event Logs
    
    $LogEntry = Get-WinEvent -LogName $log | Where-Object {$_.timeCreated -ge $TimeChange -and $_.ProviderName -eq $Provider}
        ForEach ($entry in $LogEntry){
            $EvLevel = "Level: `t" + $entry.LevelDisplayName + $nl
            $EvEventID = "EventID: `t" + $entry.ID + $nl
            $EvMessage = "Message: `t" + $entry.Message
            $EvSource = "Source: `t" + $entry.ProviderName + $nl
            $EvTime = "Time Created: `t" + $entry.TimeCreated + $nl
            #write-host $EvLevel + $EvEventID + $EvMessage + $EvSource +$EvTime + $nl

           If ($EvMessage -like '*The device, \Device\Harddisk0\DR0, has a bad block*'){
                $AllEvents += 1
            }
                        
        }

}
write-host $AllEvents


More SCCM related posts

Thursday, January 29, 2015

Using SCCM 2012 Compliance Settings to Determine Hard Drive Health

As long as your computer has S.M.A.R.T hard drive diagnostic capabilities, the following can be used to track hard drive failures.

Configuration Item(s) in SCCM 2012.


Option 1 - Using WQL query

Name - Hard Drive Predictive Failure
Platforms - All (Although, if they are virtual machines this will not work.)

Settings -
  • Name: Storage Driver Failure Prediction
  • Type: WQL Query
  • Data: String
  • Namespace: root\wmi
  • Class: MSStorageDriver_FailurePredictStatus
  • Property:PredictFailure
Compliance Rules
  • Name: Possible Hard Drive Failure is True
    • I named it this to remind me that "False" is the result we want.
  • Rule: Storage Driver Failure Prediction Equals False
  • Noncompliance severity: Critical


 Option 2 - Using PowerShell script to read system event log

This option will read the System event log and count any 'disk' source logs that are not Information level. 
Note: This will include issues with temporarily attached external hard drives or USB drives.

Name - Event Log Disk Errors
Platforms - All (This will work for servers and virtual machines)
Settings
  • Name: Report disk errors found in Event Log
  • Type: Script
  • Discovery script (See end of post)
    • Counts the events and writes that value.
Compliance Rules
  • Name: Disk error count
  • Rule: Less than 1 (That's a number one)
  • Noncompliance severity: Critical
Now that you have created the Configuration Items, you can add (one or both) to a Configuration Baseline and deploy them to your collections.

Once deployed create collections for the non compliant systems and then use that information in reports for your help desk or create a Task sequence that alerts the end user that the hard drive is may be failing.

Here's the PowerShell script...
- Updated Feb 3, 2015 to exclude '*Device\Harddisk1\DR1*' event logs.

#Version 2.30 Feb 3, 2015
#Designed for SCCM Baseline
$AllEvents = 0

#Default Event Logs
$Provider = "disk"
$HourChange = 24 #Up to you how far back you want to go
$TimeNow = [DateTime]::Now
$TimeChange = [DateTime]::Now.AddHours(-$HourChange)

#Check Event Log for errors
$EventLogNames = "System"

ForEach ($log in $EventLogNames){
    $entry = " "
    #Search Event Logs
   
    $LogEntry = Get-WinEvent -LogName $log | Where-Object {$_.timeCreated -ge $TimeChange -and $_.ProviderName -contains $Provider -and ($_.LevelDisplayName -eq 'Warning' -or $_.LevelDisplayName -eq 'Error' -or $_.LevelDisplayName -eq 'Critical')}
        ForEach ($entry in $LogEntry){
            $EvLevel = "Level: `t" + $entry.LevelDisplayName + $nl
            $EvEventID = "EventID: `t" + $entry.ID + $nl
            $EvMessage = "Message: `t" + $entry.Message + $nl
            $EvSource = "Source: `t" + $entry.ProviderName + $nl
            $EvTime = "Time Created: `t" + $entry.TimeCreated + $nl
            #write-host $EvLevel + $EvEventID + $EvMessage + $EvSource +$EvTime + $nl


            #If ($entry.LevelDisplayName -eq "Critical" -or $entry.LevelDisplayName -eq "Warning" -or $entry.LevelDisplayName -eq "Error"){
            If ($EvMessage.tostring() -like '*Device\Harddisk1\DR1*'){
                #nothing
            }
            ElseIf ($entry.LevelDisplayName -eq "Critical" -or $entry.LevelDisplayName -eq "Warning" -or $entry.LevelDisplayName -eq "Error"){
             $AllEvents += 1
            }
                       
        }

}
write-host $AllEvents



More SCCM related posts

Monday, January 12, 2015

Cleaning up CCMCache folder in SCCM 2012

One thing I have found with System Center Configuration Manager is that the client cache folder can take up a lot space on the hard drive.  Filled with updates or applications source files that no longer are needed.

Only way is to set the Cache size to smaller value using a vbs script (or powershell) that runs these commands...
set oUIResManager = createobject("UIResource.UIResourceMgr")
set oCache=oUIResManager.GetCacheInfo()
oCache.TotalSize=4096

However, if you were to install a program like AutoCAD that requires more than 4GB of cache storage, it would likely fail.

The PowerShell script listed at the bottom of this post (based on Kaido Järvemets posting at http://cm12sdk.net) will clean up the "old" content in ccmcache based on the LastReferenced WMI information. 

The '$ReferenceAge.Days -gt 30' section deletes anything not referenced in the last 30 days. 

The "#Change Cache size" section is optional and only included for reference.

The 'stop-process $pid -Force' command however is required as there have been cases where the PowerShell script fails to exit.

It can be run on a remote computer using the psexec program from Sysinternals.

Example.
copy the file over to hard drive of the remote system.
psexec.exe -s \\computername powershell -noprofile -file "c:\clear ccmcache.ps1"

Planned implementation... (other than manually running).

Compliance item for harddrives with less than x amount of free space that remediates with a PowerShell command.
or
Task sequence that runs every # of days.

$Today = Get-Date
$CacheInfoQuery = Get-WmiObject -Namespace Root\ccm\SoftMgmtAgent -Class CacheInfoEx
ForEach ($Item in $CacheInfoQuery) {
    $LastDate = [Management.ManagementDateTimeConverter]::ToDateTime($Item.LastReferenced)
    $CacheElementID = "{" + $Item.CacheID + "}"
    $ReferenceAge = $Today - $LastDate
    $FolderLocation = $item.location
   
    if ($ReferenceAge.Days -gt 30) {
        #Write-Host Found $CacheElementID Last referenced $LastDate That is $ReferenceAge ago in $FolderLocation -ForegroundColor Red
        #Based on
http://cm12sdk.net/?p=1526
  Write-Host Too old, deleting folder $FolderLocation -ForegroundColor Red
        $CMObject = New-Object -ComObject "UIResource.UIResourceMgr"
        $CMCacheObjects = $CMObject.GetCacheInfo()
        $CMCacheObjects.DeleteCacheElement($CacheElementID)
        }
    #Remove number sign from the next 2 lines to allow for more information
    #else {
  #Write-Host $FolderLocation was last referenced $LastDate  That is $ReferenceAge days ago  -ForegroundColor Green}
    }
#Change Cache size
$CacheQuery = Get-WmiObject -Namespace ROOT\CCM\SoftMgmtAgent -Class CacheConfig
$CacheQuery.Size = 1000
$CacheQuery.Put()
#Restart CcmExec service
Restart-Service -Name CcmExec

stop-process $pid -Force


More SCCM related posts