Find if Azure VM has been idle - azure

I am looking for way to find if Azure VM has been idle for over an hour.
The criteria to qualify for idle if there is no mouse movement for over an hour.
I want to save the State of VM and shut it down.
Can this be achieved using Powershell comands ?

I have configured something similar in the past, and unfortunately the only reliable way of doing this, was via a Task Scheduled on Windows (you don't specify OS in your original question). Now the following code which you should execute in PowerShell will:
If there is no activity for past 30 minutes, it will trigger a shutdown command with another 60 seconds to give user time to perform any action in order to cancel it and wait for the next triggering period. Notice the switch RunOnlyIfIdle
Start checking at 8PM for the next 12 hours every 1 hour whether there was any activity on the machine (including clicks, mouse moves, etc.), it excludes the system processes as it is build to do exactly this
RunLevel is set to 1, so OS has permissions to execute the task with no regards to the fact whether the user is still logged in or not
As you can see two last lines actually create the scheduled task, and it is configured to create it under the context of currently logged in user which executed the script, but can be adjusted if necessary
Now this will only shut down the OS, but the VM resource in Azure will only reach "Stopped" state, thus still charging you money for the compute power. In order to get it to "Stopped (Deallocated)" state and stop charging you, I had an Automation Account in Azure which had a script that was scheduled to execute every hour and scan all VMs with state "Stopped" and then it executed the command Az-StopVM which actually deallocated the VM and stopped charging money.
$TaskName = 'Idle_Shutdown'
$Service = New-Object -ComObject('Schedule.Service')
$Service.Connect()
$RootFolder = $Service.GetFolder('')
$TaskDefinition = $Service.NewTask(0)
$Settings = $TaskDefinition.Settings
$Settings.AllowDemandStart = $true
$Settings.Compatibility = 2
$Settings.Enabled = $true
$Settings.RunOnlyIfIdle = $true
$Settings.IdleSettings.IdleDuration = 'PT30M'
$Settings.IdleSettings.WaitTimeout = 'PT5M'
$Settings.IdleSettings.StopOnIdleEnd = $true
$Trigger = $TaskDefinition.Triggers.Create(2)
$Trigger.Repetition.Interval = 'PT1H'
$Trigger.Repetition.Duration = 'PT12H'
$Trigger.StartBoundary = ([datetime]::Now).ToString("yyyy-MM-dd'T'20:00:00")
$Trigger.Enabled = $true
$Action = $TaskDefinition.Actions.Create(0)
$Action.Path = 'shutdown'
$Action.Arguments = '/s /t 60'
$LocalUser = $TaskDefinition.Principal
$LocalUser.RunLevel = '1'
$user = [environment]::UserDomainName + '\' + [environment]::UserName
$null = $RootFolder.RegisterTaskDefinition($TaskName, $TaskDefinition, 6, $user, $null, 2)

Related

Output variables in Octopus -Incorrectly Passing null value

I want to pass a value in Octopus from one step to another of a project via output variable, the value is "VM running" or "VM deallocated".
There are two server, one of the server is down, another one is running so values should be passed accordingly. Now when I use the exact syntax of Output variable, it is passing Null value to next step.
Octopus deploy Project Step 1:
$RG = $RGName
$VM = "#{StepTemplate_VMName}"
$WarningPreference = 'SilentlyContinue'
$VMStats = (Get-AzureRmVM -Name $VM -ResourceGroupName $RG -Status).Statuses
$stats = ($VMStats | Where Code -Like 'PowerState/*')[0].DisplayStatus
Set-OctopusVariable -name "RunStatus" -value $stats
write-host $stats #value can either be "VM running" or "VM deallocated"
Octopus deploy Project Step 2:
$VM = "#{StepTemplate_VMName}"
$Runstatus = $OctopusParameters["Octopus.Action[Step1].Output[$VM].RunStatus"]
write-host $Runstatus
If I do not use [$VM] in the code of step 2, it give only 1 value to both the machine as "VM running"
As per the syntax given in Octopus website, we should use the VM name to pass machine specific different values.
so I used [$VM] but it gives null values to both of the machine
Edit: Event If I hardcode the value of $VM to any one VMName, it still gives me null.
Based on what I do in Octopus it should be:
$Runstatus = $OctopusParameters["Octopus.Action[Step1].Output.RunStatus"]
Even states that format on the help documentation. https://octopus.com/docs/projects/variables/output-variables
Is the step that's creating the output variable actually being run on the server that's down? When the output variable is created in a step that's run on multiple machines, multiple machine-scoped values for that variable will be created. When the subsequent step is run on the same machines, Octopus will pick the correct value based on that scope. So maybe a little off topic, but if both steps are run on both targets, specifying the $VM scope is redundant, and is only needed if you want a different machine's value.
Since hardcoding the target name here returns null it seems like the output variable creation step wasn't run on that target. I'd suggest debugging this by creating a variable called OctopusPrintEvaluatedVariables with value True in your project. That'll increase the task log verbosity and log how all variables are evaluated through each step of the deployment.

How to delete all clusters in databricks azure, not searching and copying for cluster_id?

I would like to pragmatically delete set of clusters. Let's say, they are only one purpose for some testing and I'd like to delete them at once.
I thought maybe it is possible, when I assign all clusters to the specific pool, but it seems Pool API does not return info about clusters assigned to it.
Any other ideas?
thx
You can do that easily in PowerShell using this module. I tested this script below and worked like charm
$databricksBearerToken = "" # enter your manually generated token here
$databricksRegion = "" # enter region for the workspace here
$databricksWorkSpaceName = "" # enter the name of your databricks workspace
# Return a list of existing clusters
$myclusters = Get-DatabricksClusters -BearerToken $databricksBearerToken -Region $databricksRegion
# Iterate through these clusters and remove them one by one
Foreach($cluster in $myclusters)
{
$clusterName = $cluster.cluster_name
$clusterID = $cluster.cluster_id
Write-Host $clusterName
Remove-DatabricksCluster -BearerToken $databricksBearerToken -Region $databricksRegion -ClusterName $clusterName -ClusterId $clusterID
}

Powershell script only starts first VM in list

I have an Azure powershell script to start a list of VMs. When it runs it sucessfully starts the first VM in the list then nothing. No errors are reported, it appears to never return from the start command
Code as follows:
$vmList = "VM1", "VM2", "VM3"
Write-Output "Starting VMs in '$($AzureResourceGroup)' resource group";
Foreach ($vm in $vmList)
{
Write-Output "Starting VM...$vm"
$result = Start-AzureRmVM -Name $vm -ResourceGroupName AzureResourceGroup
Write-Output "Result of Start VM...$result"
}
When this run it outputs the "Starting VM....VM1", it starts the VM then nothing...
It sounds like your Start-AzureVM call is simply waiting for the VM to finish starting up.
That is, Start-AzureVm is by default a blocking, synchronous operation - despite its name[1]
To make it asynchronous, use the -AsJob switch, which uses a background job to start the VM; the call ends once that job has been created and returns a job object that you can use to track start-up progress later, via the usual *-Job cmdlets, such as Receive-Job.
[1] Regarding the name Start-AzureVM:
It is a misnomer in that PowerShell's nomenclature calls for the Start verb to exhibit asynchronous behavior:
From https://learn.microsoft.com/en-us/powershell/developer/cmdlet/approved-verbs-for-windows-powershell-commands (emphasis added):
Invoke vs. Start
The Invoke verb is used to perform an operation that is generally a synchronous operation, such as running a command. The Start verb is used to begin an operation that is generally an asynchronous operation, such as starting a process.
Note that the core Start-Sleep cmdlet is similarly misnamed.
With synchronous-by-default behavior, there is no good naming solution, because the name Invoke-AzureVm would be confusing.
The better approach - which would obviously be a breaking change - would have been to make the cmdlet asynchronous by default and offer a -Wait switch for to opt-into synchronous operation.

How to Create Indefinite Duration for a Task in Windows 2003 Task Scheduler

I'm trying to create tasks with an indefinite duration. On Windows Server 2008, if I don't specify the duration, then the duration is assumed infinite. However, on Windows Server 2003 a duration must be specified (and be equal or greater than the interval). Currently, I am just setting the task to have a duration of a year if it is meant to repeat a long time.
Is there a way to specify an indefinite duration on Windows Server 2003?
if (m_TimeInterval.m_Expire.Year < 2099) //xp and win7 max year is 2099
tTrigger.Repetition.Duration = m_TimeInterval.getDuration();
else if (!newVer) tTrigger.Repetition.Duration = m_TimeInterval.getInterval().Add(TimeSpan.FromDays(365));
tTrigger.Repetition.Interval = m_TimeInterval.getInterval();
tTrigger.Repetition.StopAtDurationEnd = true;
Notes:
I am using the Task Scheduler API to add these task definitions.
Interval and Duration are set by use of TimeSpans.
newVer is false since I am working with Task Scheduler 1.1 and not 1.2 or higher
tTrigger is of type TimeTrigger
I ended up using 1-1-2099 as 'indefinite'
You also can use TimeSpan.Zero which makes the task infinite.
Heres a short code snippet to demonstrate how to use it on a Trigger :
Trigger triggerTask = Trigger.CreateTrigger(TaskTriggerType.Time);
triggerTask.StartBoundary = DateTime.Now.AddMinutes(3);
triggerTask.SetRepetition(new TimeSpan(0, 3, 0, 0, 0), TimeSpan.Zero);
Can you use Duration.Forever.TimeSpan?

IIS 6 ServerBindings with WMI and Powershell v2

I've long had a bunch of VBS automations for IIS 6, including one that gets/sets complex server bindings on several farms of paired servers, each having dozens of apps, each app having 3-12 host headers. Each app has hostname, hostname-fullyqualified, and Disaster Recovery enabled hostname, so they can be a mess to maintain manually.
I did all my vbs stuff using ADSI, but I'm thinking WMI is probably more flexible than ADSI from a full server maintenance perspective. Please correct me if I'm wrong. So now I'm trying to move up to PowerShell + WMI to prepare for Windows 2008 + IIS 7.5. I'm enjoying the learning process, but I've hit a roadblock on this problem.
I can get/set all properties via WMI on my IIS 6 web servers, except ServerBindings. I feel like I'm close, but I'm missing some layer of containment, and I just can't get the objects I'm building to cast over to the right automation object.
The following code gets and reads the ServerBindings just fine. I simply can't figure out a way to write my changes back. Any advice is welcomed.
$objWMI = [WmiSearcher] "Select * From IISWebServerSetting"
$objWMI.Scope.Path = "\\" + $server + "\root\microsoftiisv2"
$objWMI.Scope.Options.Authentication = 6
$sites = $objWMI.Get()
foreach ($site in $sites)
{
$bindings = $site.psbase.properties | ? {$_.Name -contains "ServerBindings"}
foreach ($pair in $bindings.Value.GetEnumerator())
{
# The pair object is a single binding and contains the correct data
$pair
$pair.IP
$pair.Port
$pair.Hostname
# And this line will successfully erase the contents of
# the ServerBindings
$bindings.Value = #{}
# but I can't figure out what to do to update $bindings.Value
}
$site.Put()
}
I'm liking Powershell so far, so thanks for any help you're able to offer.
Alright. I got distracted with major disk failures. The fun never stops.
Anyway, the solution to this problem is simpler than I'd made it:
process
{
$bindings = $_.ServerBindings
foreach ($binding in $bindings)
{
$binding.IP = $ip
$binding.Port = $port
$binding.Hostname = $hostname
}
$_.ServerBindings = $bindings
$_.Put()
}
ServerBindings is an array, but it likes to be an array of its own kind. I was trying to build the array from scratch, but my home-rolled array didn't smell right to Powershell. So, pull the array out of ServerBindings into a new variable, manipulate the variable, then assign the manipulated variable back to the ServerBindings property. That keeps all the right typing in place. It's smooth as silk, and seems easier than ADSI.

Resources