Turing Ping On/Off With An SCCM Configuration Item

Details

The best way I’ve found to disable ping using a configuration item through a
script is using a .Net class. Our fleet is Windows 7+ and Server 2003(ick)+ so
the solution needs to be more robust than using the Server 2012+ built-in
firewall cmdlets.

The scripts are quite simple but we rely heavily on the following core code:

# Get the firewall manager object from .Net
$Firewall = New-Object -ComObject HNetCfg.FwMgr

# Get the domain policy (0)
$Policy = $Firewall.LocalPolicy.GetProfileByType(0)

# Get the settings for ping
$IcmpSettings = $Policy.IcmpSettings

This code gets the firewall manager object out of .Net and then grabs the domain
Firewall policy then the ICMP settings for the domain policy. This is flexible
so the .Net object should exist in older versions of Windows.

Now, there is really only one setting we care about in the ICMP Settings:

$IcmpSettings.AllowInboundEchoRequest

If it’s $true then ping is turned on, if it’s $false then ping is disabled.

Thus, the discovery script is fairly simple, all we have to do is .ToString()
the $IcmpSettings.AllowInboundEchoRequest and if we want to enable/disable
ping all we have to do is add an = $true or = $false to the statement.

Below I’ve put the full scripts I used for our code.

Scripts

Discovery

$Firewall = New-Object -ComObject HNetCfg.FwMgr

$Policy = $Firewall.LocalPolicy.GetProfileByType(0)

$IcmpSettings = $Policy.IcmpSettings

$IcmpSettings.AllowInboundEchoRequest.ToString()

Remediation – Turn Ping On

$Firewall = New-Object -ComObject HNetCfg.FwMgr

$Policy = $Firewall.LocalPolicy.GetProfileByType(0)

$IcmpSettings = $Policy.IcmpSettings

$IcmpSettings.AllowInboundEchoRequest = $true

Remediation – Turn Ping Off

$Firewall = New-Object -ComObject HNetCfg.FwMgr

$Policy = $Firewall.LocalPolicy.GetProfileByType(0)

$IcmpSettings = $Policy.IcmpSettings

$IcmpSettings.AllowInboundEchoRequest = $false

Powershell and the VMWare 6.5 RestAPI

VMWare has released a RestAPI to be an alternative to powercli and their other SDKs.  Once of the best ways to peek at the API is via the API Explorer.  In a browser, open the web page to your vcenter server/appliance: https://<vcenter-FQDN>/apiexplorer/

I found plenty of references to using the API is other languages/formats …… but as usual powershell was harder to come by.  Also, in general I found examples to be overly simplified and vmware’s explanation of what data should look like to be lacking.  So after a fair amount of trial and error I put together a number of basic functions and put them in a module.  They are public in Github.

The module is not (as of yet anyways) a complete covering of everything.  It covers basics around creating and removing VMs, tagging, and of course Authenticating.  The number of options around things like creating the vm are numerous therefore I did use some defaults.  That being said,  if you look at the code in the functions along with the apiexplorer you should have a good bases for making changes to make it do what you want.  This applies to all the functions.  As with most RestAPIs, once you figure out the basic structure of the various methods (GET,POST,DELETE,PUT,PATCH) and how to construct the body (in this case its just JSON), expanding to consume any part of the api becomes much easier.

 

Emergency! We need that patched!

Infrastructure is never a perfect world when it comes to Microsoft patch management. Is your WSUS service healthy? Is it integrated to SCCM as a software update point? Are you free of errors, but still not sure? Did that security patch really go out?

Your security monitoring, SCOM, or log analytics tool might tell you otherwise; and what are you left to do?

Do you believe the SCCM deployment reports, or your monitoring that says a critical patch is missing?

No matter the case of what, or why – sometimes there comes a point where you just need to brute force install a patch. Also, you need it, like, NOW.

You’ve got options if you’re prepared with DSC, but this is production, and we need them patched now – reboot later! Business requirements… alas.

Hopefully you can pull a dynamic list of systems from SCOM, SCCM, VMWare, Hyper-V, AD, or where ever… and just pipe it through. Obviously, if you need to reboot now as well, that is easy enough to modify from this little one-off.

Prep:
1. Get a list of systems you need to apply the patch to.
2. Extract the .cab of the KB you download from Microsoft.
3. Assumes you have remote powerShell / Admin access.

$listOfComputers| foreach {
$session = new-pssession $_
copy 'path to patchKB.cab' 'path to patchKB.cab' -tosession $session

Invoke-Command -session $session -scriptBlock {
## Check if KB is already installed
$KB = get-hotfix |where{$_.hotfixid -eq 'KB#####'}
if (!$KB) {

## Just in case you need to verify the OS version ##
$os = (Get-WmiObject -class Win32_OperatingSystem).version

## dism... I know -- allows for remote execution using no new processes, or EULA to the KB ##
dism.exe /online /add-package /PackagePath:c:\windows\temp\patchKB.cab /norestart
}

Else {write-host 'KB previously installed'}}}

Install chef client on Windows Container and Connect to Chef Server

With a traditional windows machine the traditional “knife bootstrap windows winrm …” approach to bootstrapping works fine.  It is not however so straight forward for a container.  When you first spin up a container (assume a blank server core)  you have two methods to interact with it, Direct Powershell from the container host or the docker client, neither of which work with “knife” (in so far as I know).  You also don’t know the admin password.  There are going to be different methods to solving this problem, this is just one example that I have found to be simple and easy.  I am assuming you already have a chef server setup and have a minimal amount of familiarity with it.

Step one: Download the Validation key into a file named “validation.pem” and store it in an empty folder.  Then create a file ‘first-boot.json’ in that same folder. Contents:

{“run_list”:[]}

Of course you can add additional parameters to this file as you see fit.

You’ll also need to know your ‘chef_server_url’ and ‘validation_client_name’ (which you can get from “Generate knife Config” in the chef server.

Step two:  Spin up a container (more details on this here)

$ps = "Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/Microsoft/Virtualization-Documentation/master/windows-server-container-tools/Wait-Service/Wait-Service.ps1' -OutFile 'c:\Wait-Service.ps1';c:\Wait-Service.ps1 -ServiceName WinRm -AllowServiceRestart"
($cid = docker run -d microsoft/windowsservercore powershell.exe -executionpolicy bypass $ps)

Step three: Install the chef client and connect to chef server.  There are 3 lines to update with your specific info

Invoke-Command -ContainerId $cid -RunAsAdministrator -ScriptBlock{Invoke-WebRequest -uri "https://omnitruck.chef.io/install.ps1" -OutFile c:\install.ps1;c:\install.ps1;Install}
$cPath = 'c:\'
$local = '' # update this from above
docker cp -L $local $cid`:$cPath
$chefURL = '' # update with your chef server URL
$validationClientName = '' # update this with your info
Invoke-Command -ContainerId $cid -RunAsAdministrator -ScriptBlock{
@" chef_server_url '$using:chefURL'
validation_client_name '$using:validationClientName'
file_cache_path 'c:/chef/cache'
file_backup_path 'c:/chef/backup'
cache_options ({:path => 'c:/chef/cache/checksums', :skip_expires => true})
node_name '$env:COMPUTERNAME'
log_level :info log_location STDOUT "@ | Out-File 'c:\chef\client.rb' -Encoding utf8 -Force
chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json
}

That's it.  Run the following if you want to inspect the client.rb that is create for troubleshooting

$name = (((docker ps --no-trunc -a| Select-String $cid).ToString()).Normalize()).Split(" ")[-1]
$ps = "get-content c:\chef\client.rb"
docker exec $($name) powershell.exe -executionpolicy bypass $ps

Powershell: Finding All Traverse Groups

When it comes to managing file shares one of the larger issues I deal with on a regular basis is identifying all the traverse groups that lead to a specific folder.

The first thing to do is identify what makes a traverse ACL.  Where I work we simply use Read and Execute rights and then apply them to the folder only, not inheriting further down the tree.

I first build those into variables I can use later on:

# Build tests for traverse group
$TravRights = [System.Security.AccessControl.FileSystemRights]"ReadAndExecute,Synchronize"
$TravInheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None
$TravPropagationFlag = [System.Security.AccessControl.PropagationFlags]::None
$TravType = [System.Security.AccessControl.AccessControlType]::Allow

In order to properly handle the full path we’ll identify the root of the path.  When it’s a network share that’s grabbing the third entry of the array created from the “string”.split(“\”) as the first two entries are blank from the front double backslashes.  Otherwise I’m assuming it’s a normal filesystem provider which can simply have it’s qualifier split from the path.

# Get the root of the path
if($Path.StartsWith("\\")) {
    $RootPath = "\\" + [string]::Join("\",$Path.Split("\")[2])
} else {
    $RootPath = Split-Path -Path $Path -Qualifier
}

Next, you need to build a list of the directories leading to the directory in question.  The first thing I do is pull apart the string using String.Split().  Next I use the Split-Path cmdlet to identify the root drive.  We also need to pull out the root of the drive so all we’re getting is a list of the directories in order.

Also, we’ll create a path’s arraylist which we’ll use to parse through the directories and create a list of all thee paths not just the directories.  So if we pass into the function C:\Foo\Bar we get “C:\Foo”, “C:\Foo\Bar back as an array list.

# Split up paths and pull the root
$spath = $Path.Replace("$RootPath\", "").Split("\")
$Paths = New-Object System.Collections.ArrayList

# Build a list of all the directories that lead to the target directory
for($i = 0; $i -le $spath.Length; $i++) {
    if($spath[$i] -ne $null) {
        $PathToAdd = ""
        for($j = 0; $j -le $i; $j++) {
            $PathToAdd += "$($spath[$j])\"
        }
        # Add the new path to our list of paths
        $Paths.Add("$RootPath\$PathToAdd") | Out-Null
    }
}

Once we have our list of paths to check we can simply loop through the paths then gett the ACL for each path.  Once we have the ACL we can loop through the Access rules in each ACL and then check the access rule against the flags we defined earlier which are what we’re looking for.

# Loop through the paths and determine which contain a traverse group
foreach($item in $Paths) {
    $itemacl = Get-Acl -Path $item
        foreach($acl in $itemacl.Access) {
        # Check the acl for the traverse permissions defined earlier
        if(($acl.InheritanceFlags -eq $travInheritanceFlag) -and ($acl.PropagationFlags -eq $travPropagationFlag) -and ($acl.FileSystemRights -eq $travRights) -and ($acl.AccessControlType -eq $travType) -and ($acl.IsInherited -eq $false)) {
            # We've now found a traverse group
            $SamAccountName = $acl.IdentityReference.ToString().Split("\")[1]

            # Make sure the account name isn't null, then get the group make sure it exists in AD then add the path to the group output object.
            if($null -ne $SamAccountName) {
                $ADObject = Get-ADGroup -Identity $SamAccountName
                if($ADObject -ne $null) {
                    $TraverseGroups += $ADObject | Add-Member -MemberType NoteProperty -Name TraversePath -Value $item -Force -PassThru
                }
            }
        }
    }
}

Once we’ve identified that we’ve found exactly what we’re looking for and that the account name isn’t null we can add it to our $TraverseGroups array.

The full code can be obtained from my GitHub repository Find-TraverseGroups.