Sunday, May 12, 2019

Ivanti Service Manager and PowerShell? Oh YEAH!

I discovered the excellent PSHEATAPI a few months ago. Ever since then whenever I have had a spare minute I have been poking around inside the API interface in our ISM tenant.

It wasn't long until I had a light bulb moment. Where I finally truly appreciated how well designed mature Enterprise software can be. Unlocking this API is a game changer for us.

Let me give some background. I was an early adopter of PowerShell. Over the last decade, I've enjoyed building tools that people in my teams have been able to use to streamline their work and make life easier.

I always faced the problem of not being able to put those tools in front of our normal user base. 4 years ago I knuckled down and learnt MVC and c# so I could do that. I built some rudimentary web interfaces that wrote back to a SQL database then read from that database in PowerShell to initiate automation scripts.

This never really gained traction beyond the first few that I built because it simply took too long and was quite labour-intensive. So time to market for new features was too long and it was never really worth investing in.

That all changed when I realised ISM and its request offering module could be called from a simple PowerShell script.

That was my lightbulb moment! When I realised the service management platform could serve as the user interface. While it could also be the backend and a system of record so we know who did what when!

Over the past couple of months, I've been building out a simple framework. The idea is to make it quick and easy to create a form in Ivanti Service Manager that our users can enter values into. Then pull that form from PowerShell and feed user input into some self-service automation script.

Well, it's built now and running 8 service requests in production.  Our time to market for a new self-service feature is now about an hour, down from 1 to 2 weeks. Making self-service features a great time investment.

I'm really excited about this. I don't know if this is old territory for a lot of people. The combination seems really obvious now that I'm looking at it. If it's not already in widespread use in large IT shops this or something like it will be soon.

If you're already a customer of Ivanti service manager in the Cloud and have a large Library of PowerShell scripts you should have a look at this. You don't have to do it the way we're doing it here. But if you only take one thing away from reading this. Let It Be the knowledge that;

It is now ridiculously easy to build enterprise-grade self-service applications.

A copy of the framework is available on GitHub. https://github.com/benhaslett/OhBe

Monday, June 5, 2017

Everyday Powershell - Part 43 - Automatically Resume Failed HyperV Replication

Everyday Powershell, as useful as HyperV Replication.

So on our 2012R2 HyperV boxes we occasionally get problems with replication. Usually after a Windows Update related reboot.


So we could go through each of the VMs failed Replications, right click, resume replication but what are we? Barbarians?!?

We've scheduled the following script  to run on our hypervisors;

$hypervisor = "someserver"

$vms = Get-VMReplication -ComputerName $hypervisor | where health -eq "critical" | where state -ne replicating | where name -ne "someservertoexclude"

foreach($vm in $vms){
    while((Get-VM -ComputerName $hypervisor -VMName $vm.name | where ReplicationState -ne "disabled" | get-vmReplication).state -ne "Replicating"){
        $vm.name
        (get-VM -ComputerName $hypervisor -VMName $vm.name | where ReplicationState -ne "disabled" | get-vmReplication).state
        if ((get-VM -ComputerName $hypervisor -VMName $vm.name | where ReplicationState -ne "disabled" | get-vmReplication).state -ne "Resynchronizing"){
            get-vm $vm.name -ComputerName $hypervisor | Resume-VMReplication -Resynchronize
            get-vm $vm.name -ComputerName $hypervisor | Resume-VMReplication -Continue
            get-vm $vm.name -ComputerName $hypervisor | Resume-VMReplication
            get-vm $vm.name -ComputerName $hypervisor | get-VMReplication
        }
        start-sleep -Seconds 10
    }
}

get-vm -ComputerName $hypervisor | where health -eq "warning" | foreach-object {Reset-VMReplicationStatistics $_.name}

So now when replication goes to hell because of a reboot, all the VMs just politley get back in line and do as they're told!

Friday, May 5, 2017

Everyday Powershell - Part 42 - Creating and populating Groups

Everyday Powershell, as useful as a loosely typed variable. In that sometime it's great others it can ruin your day!

So we're following on from last time where we learned the resolution of all the monitors we could talk to.

Now we're going to do something with that data.
import-csv C:\temp\monitoraudit.csv | where pcon -eq $true | where ScreenWidth -ne "" | ForEach-Object{
    $temp = "" | select pc, resolutionstring
    $temp.pc = $_.computername
    $temp.resolutionstring = ("PCs with screens at " + $_.ScreenWidth + "x" + $_.ScreenHeight)
    $temp
    $test = $null
    $test = get-adgroup $temp.resolutionstring
    if($test -eq $null){
        New-ADGroup -GroupScope DomainLocal -Name $temp.resolutionstring
    }
    Add-ADGroupMember $temp.resolutionstring -Members (get-adcomputer $temp.pc)
}
 
We use another foreach-object to jump over each row in our CSV
  • setup another temp object 
  • fill the temp object if useful information, PC name and a string with the resolution
  • we check if an AD group with the name of our string exists
  • if it doesn't we create the group
  • then we add the PC Name to the AD group
So what can we do with this? Well it's really useful to had AD groups with all your machines resolutions. You can;
  • See who management likes
  • Create interesting reports on which monitors should be replaced
  • Use the groups to apply policies to
The last one is what we're doing this for. We'll use these groups to target customised wallpapers, screen savers and login screens.

Friday, April 28, 2017

Everyday Powershell - Part 41 - Get Screen Resolution from remote PCs

Everyday powershell. It's not updated everyday, it's tools you could use daily.

You know sometimes you just need to know what resolution all your computers are running.
get-adcomputer -Filter {operatingsystem -like "Windows 7*"-Properties operatingsystem | ForEach-Object {
    $temp = "" | select computername, ScreenHeight, ScreenWidth, pcon
    $temp.pcon = Test-Connection $_.name -Count 1 -Quiet
    $temp.computername = $_.name
    if($temp.pcon){
        $resolution = Get-WmiObject -ComputerName $_.name -Class Win32_DesktopMonitor
        $temp.ScreenHeight = $resolution.ScreenHeight
        $temp.ScreenWidth  = $resolution.ScreenWidth
    }
    $temp
    $temp | export-csv C:\temp\monitoraudit.csv -Append
}

Check out the filter on the first line, that can be anything. We just needed Windows 7 machines.

It's a pretty easy foreach-object;

  • sets up a temp object in my favorite manner,
  • pings the machine to make sure it's up,
  • pulls the info we need from WMI
  • then bangs it out to the console and a CSV.
Hitting up each machine with a WMI query gets us fast results but it's not exactly complete. You could rejig this so that it ran as part of a login script or schedule it to run daily.

Why do we need this info? Well that's for next time when we'll do something useful with this data set we've created.

Thursday, November 24, 2016

Windows 10 Delivery Optimization Service

Ok so how the heck didn't I know this was a thing?

https://privacy.microsoft.com/en-US/windows-10-windows-update-delivery-optimization

From that site

How is my PC used to send apps and updates to other PCs?
Delivery Optimization downloads the same updates and apps that you get through Windows Update and the Windows Store. Delivery Optimization creates a local cache, and stores files that it has downloaded in that cache for a short period of time. Depending on your settings, Windows then send parts of those files to other PCs on your local network or PCs on the Internet that are downloading the same files.

So I'm essentially seeding windows updates from my home PC? Which is great and all, a really efficient use of resources... For the people trying to distribute the updates.
Me I guess I've gotta suck up the rest of the month (22 days) with no download allowance. How generous of me.

Running with the powershell theme that we're all about here... Here's how you would stop that;

Get-Service DoSvc | Set-Service -StartupType Disabled

Sunday, April 17, 2016

Everyday Powershell - Part 40 - Get folder size

So my daughter (10 months) and I were hanging out on the couch listening to Pandora. I encourage her to play with the media center keyboard and she was doing that with gusto. When WHAM! Media Center PC hangs and when it reboots is unable to start the operating system.

We did a full power cycle and it came back fine, but reported low disk space. So it looks like my little girl hit her first problem with a full disk and windows hibernation. I'm so proud!

Her spamming had apparently asked the computer to hibernate. It tried but ran out of disk. So after getting back into the OS we needed to do a bit of a review and free up some space. At this point it dawned on us what our next part of Everyday Powershell was going to be about!

function get-foldersizes{
    param($startFolder )

    $report = @()

    $count = 0
    $colItems = ((Get-ChildItem $startFolder ) + (Get-ChildItem $startFolder -Hidden))
    foreach ($i in $colItems){
        write-progress -activity "Coutin Files" -Status $i.fullname -percentcomplete (($count / $colitems.count) * 100)
        if($i.PSIsContainer){
            $subFolderItems = (Get-ChildItem $i.FullName -File -Recurse | Measure-Object -property length -sum)
        }
        else{
            $subFolderItems = $i.FullName | Measure-Object -property length -sum
        }
        $temp = "" | select folder, size
        $temp.folder = $i.FullName
        $temp.size = [math]::round(($subFolderItems.sum / 1MB),2)
        $report += $temp
        $count++
    }
    $report | sort size
}

Super simple function that gets the size of all folders in $startfolder if that's null it just grabs the current folder.

So once the function was up and running my girl and I found an old copy of marvel heroes taking up 11gb. We deleted that and got back to the important business of playing with blocks!

Reference - We used this technet article as the basis for our script.


Monday, March 7, 2016

Everyday Powershell - Part 39 - Scheduling a Powershell Script in powershell

We've covered this ground before back in the early parts of everyday powershell. But now if you've got at least windows 8 or server 2012 there's actually really good support in the shell for scheduled tasks now.

We powershell enthusiasts spend a lot of time scheduling jobs to run scripts of various sorts. That's a lot of repetitive work. You know what? If you are doing a job manually a lot, might be a good idea to script it.

Here's a quick function that wraps up the new-scheduled task commands and makes it quick and easy to schedule a powershell script.

function new-scheduledscript{
    param(
        [Parameter(Mandatory=$true)]
        $scriptpath,
        [Parameter(Mandatory=$true)]
        $user,
        [Parameter(Mandatory=$true)]
        $password,
        [Parameter(Mandatory=$true)]
        $time,
        [Parameter(Mandatory=$true)]
        $taskname
    )
    $A = New-ScheduledTaskAction –Execute C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe -Argument "-file `"$scriptpath`""
    $T = New-ScheduledTaskTrigger -Daily -At $time
    $S = New-ScheduledTaskSettingsSet
    $D = New-ScheduledTask -Action $A -Trigger $T -Settings $S
    Register-ScheduledTask $taskname -InputObject $D -User $user -Password $password
}

Pulled  the syntax for this from the documentation over here; https://technet.microsoft.com/en-us/library/jj649810(v=wps.630).aspx