Do I need to check instancestatus before I start or stop an azure VM? - powershell

I'm automating an Azure Virtual Machine using powershell, just starting and stopping one machine on a schedule. I've done this before, but I came across this code snippet which has an extra step, and I want to make sure I'm not missing something important:
# Shutdown VM(s)
$vmList = ('VM1', 'VM2', 'VM3')
$svcName = 'servicename'
For ( $vmCount = 0; $vmCount -lt $vmList.Count; $vmCount++) {
$vm = Get-AzureVM `
-ServiceName $svcName `
-Name $vmList[$vmCount]
if ( $vm.InstanceStatus -eq 'ReadyRole' ) {
Stop-AzureVM `
-ServiceName $vm.ServiceName `
-Name $vm.Name `
-Force
}
}
So I would have just called Stop-AzureVM... What does the check to InstanceStatus do? Does it, say, prevent the VM from shutting down if it's in the middle of installing updates? I'm thinking no, and that this is a check that's more important for other commands. But now I want to know.
I tried searching around and found it used in several unrelated code samples, but I've been unable to find an explanation.

ReadyRole is the steady state for an Azure VM. It essentially means it's not starting, stopping, provisioning, transitioning, stopped etc.
To me, I think the line $vm.InstanceStatus -eq 'ReadyRole' is just a basic check on the machine status. If you try to shutdown, or run any other command on the VM whilst it is busy doing something, your command will fail with an error anyway.
I just ran a test trying to stop a VM after I'd started it from the web management console and this is what I received:
stop-azurevm : ConflictError: Windows Azure is currently performing an operation with x-ms-requestid
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx on this deployment that requires exclusive access.
At line:1 char:1
In this case it's because the status was most likely starting
However, once the VM is stopped, issuing another stop command (whilst daft) works without any apparent problem.
PS > get-azurevm
ServiceName Name Status
----------- ---- ------
vm cloudservice StoppedDeallocated
PS > stop-azurevm -servicename cloudservice -name vm
OperationDescription OperationId OperationStatus
-------------------- ----------- ---------------
Stop-AzureVM xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Succeeded
So, in conclusion, I'd say it's a tidy bit of scripting diligence to avoid pointless / impossible operations during the script execution.

Related

Stop a service so it can be uninstalled

I am doing some automated uninstalls of Autodesk software, and Autodesk has once again screwed the pooch with their uninstalls. Their uninstall is supposed to do reference counting on certain shared components, like their Single Signor Service, Autodesk Genuine Service, Licensing service, etc. The problem is, when you are uninstalling that last ADSK product, the uninstaller is too stupid to stop the service, so their uninstaller fails with a 1603 fatal error. Last year you could stop the services before you started the uninstall, but this year I am getting this error
Stop-Service : Service 'Autodesk Access Service Host (Autodesk Access Service Host)' cannot be stopped due to the following error: Cannot open Autodesk Access Service Host service on computer '.'.
When using this code
Get-Service -Name "Autodesk Access Service Host" | Stop-Service -Force -NoWait
I have verified with
(Get-Service -Name "Autodesk Access Service Host").CanStop
that service can be stopped. At least according to the property.
I also tried
Start-Process "$env:WINDIR\system32\sc.exe" \\.,stop,"Autodesk Access Service Host" -NoNewWindow -Wait
while ((Get-Service -ComputerName '.' -Name "Autodesk Access Service Host" |
Select -ExpandProperty Status) -ne 'Stopped') {
Write-Host "Waiting for service to stop..."
Start-Sleep -Seconds 10
}
And that has run for 15 minutes with no results. Interestingly I CAN disable the service, but I really don't want that. I just want to stop it temporarily, so IF the Autodesk uninstall that is running is the last one with a dependency on this service will uninstall it correctly and returns the correct exit code of 0.
EDIT: I tried
sc stop "Autodesk Access Service Host"
from an elevated command prompt and that shows
STATE : 3 STOP_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
so not really sure how to take STOP_PENDING along with NOT_STOPPABLE, nor why this would say NOT_STOPPABLE when the property above shows true.

PowerShell to stop and disable a service. Reading the servers from a TXT file

I see some questions about this topic, but I cannot get it working
Get-Service -Name Spooler -ComputerName (Get-Content c:\tmp\scripts\Servers\iservers.txt) |
Stop-Service -PassThru | Set-Service -StartupType Disabled -whatif
The code executes for each server on the txt file, and stops de service, but not disable the service.
Any help to get it work and/or Troubleshooting???
Regards.
How to approach this kind of problem
In automation, we work up to complexity, meaning you should start simply and then add on more features until you see where it breaks.
Right now, you're trying to do a bunch of operations in one single line:
Load a list of computers and
Reach out to the computers and Stop a service and
Also while doing this, set the service to not automatically start.
There are a lot of problems you can run into, like "what happens if these PCs aren't enabled for remoting", or "what if you need a different account to handle stopping or disabling a service".
When you're trying to figure it all out in one-line, you're in for a bad and frustrating time.
How to fix it
Start simply. Start with one computer that's nearby and definitely turned on.
Begin with reading a service. Can you even get this operation to run?
Get-Service -ComputerName SomePC123 Spooler
Status Name DisplayName
------ ---- -----------
Running spooler Print Spooler
If you run into an error, then first figure out how to be able to remote into that one PC and see if the Print Spooler is running. Then, you will know what steps to deploy to all of your machines to prepare them for remoting.
Then, once you can check if a service is running, you can add on the next step, try to stop the service.
So your code would start to look like this:
$computers = get-content .\someTextFile.txt
forEach($computer in $computers){
$service = Get-Service -ComputerName $computer Spooler
"status of spooler on $computer is $($service.Status), with start type of $($service.StartType)"
#todo, set start type to Disabled...
}
Eventually, you will have migrated each step out of the one-liner and you'll know where and why any given command is failing. This is the way.

Using Powershell to pull services on multiple domain computers

My code currently is:
$Workstationlist=get-adcomputer -filter * -searchbase 'OU=Workstations, DC=example, DC=com' -SearchScope 2 | foreach {$_.Name}
foreach($workstation in $Workstationlist){
get-service -ComputerName $workstation -name wauaserv
}
It seems to pulling service statuses from the array that is being parsed by the foreach method, but after a few values are returned it will spit this error message in between which seems to point at the location I saved the script
Running wuauserv Windows Update
Stopped wuauserv Windows Update
Running wuauserv Windows Update
Stopped wuauserv Windows Update
get-service : Cannot find any service with service name 'wuauserv'. At
\locationIsavedthescript
The idea is that I will stop the wauaserv service on all workstations in the domain, I was then going to add lines to delete everything from the \windowsdistrobution\ folder. And then restart the service. This effectively kills any downloading pending install update. I can drop $workstation into a ls snippet easy enough but pulling services doesn't seem to work quite as clean. Not sure why it is point the get-service to the location that I saved the script.
Any ideas and explanations as to how to accomplish what I'm shooting for a bit more cleanly or at least hide the erroneous output since the command IS running against the array.
I hope this made sense to someone out there.

PowerShell Hyper-V VM creation and boot

I am attempting to use PowerShell to create and start a VM:
$vmName = "vm" + (Get-Date -Format "yyyy-MM-dd-HH-mm")
New-VM -Name $vmName -NewVHDPath "$vmName.vhdx" -NewVHDSizeBytes 64GB -MemoryStartupBytes 8GB -Path $vmName -Generation 2
# Attach the Windows 10 ISO as a DVD drive to the VM
Add-VMDvdDrive -VMName $vmName -Path win.iso
# Set correct boot order (DVD drive first)
$dvd = Get-VMDVDDrive -VMName $vmName
Set-VMFirmware -VMName $vmName -FirstBootDevice $dvd
# Start the VM and connect to it
Start-VM -Name $vmName
vmconnect $env:COMPUTERNAME $vmName
This works well, creates the VM, attaches the Windows 10 ISO, sets the correct boot order, starts the VM and connects to it.
However, right after starting up, the VM fails to boot. First, a black screen prompting me to press any key to boot from the DVD pops up, however, it is only for a brief second and before I manage to do it, I get this screen:
The boot order is correct in the VM settings: DVD first, then network, then the VHD. I want to boot from the DVD, but I don't have a chance to press a key to do so before the white screen appears.
Morever, I want to make the script so that it automatically enters the DVD/ISO boot without me having to press a key to enter that boot option. How can I do that? My ultimate goal is a completely unattended installation which starts by invoking the scripts and ends by the installed Windows 10 guest executing a PowerShell script shared from the host. That means I can't ask the user to press a key at a certain time to help the boot sequence along.
Edit: Bounty of 50 points for someone who can demonstrate a PowerShell script which creates, starts, connects and boots a Hyper-V VM with a Windows 10 ISO so that at the end there is a running VM on the first screen of the Windows 10 installer.
I think your issue is not a powershell issue. Your issue is that you are using a regular boot iso, but in your case you need to create a custom WIM (Windows Imaging File). If you create one you can start directly the installation process without human interaction.
I don't want to duplicate the text written already on superuser.com; it is long and takes many steps. I recommend using the second approach - Creating custom ISO from Windows 10 as it makes sense and takes you step-by-step over the creating custom WIM.
Try this:
# Start the VM and connect to it
vmconnect $env:COMPUTERNAME $vmName
Start-VM -Name $vmName
Start-Sleep -Seconds 1
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
You might have to adjust the sleep time, so a custom wim is probably the better option.

Remove-AzureDisk throws error, not sure why

I have an Azure VM and I'm trying to delete it using Powershell. I also want to remove the disk that that VM OS was on (there are no data disks).
I assume I'm going to need the following two cmdlets:
Remove-AzureVM
Remove-AzureDisk
Here's my code:
$VMs = Get-AzureVM $svcName
foreach ($VM in $VMs)
{
$OSDisk = ($VM | Get-AzureOSDisk)
if ($VM.InstanceStatus -eq "ReadyRole") {
Stop-AzureVM -Name $VM.Name -ServiceName $svcName
}
remove-azurevm -ServiceName $svcName -Name $VM.Name
Remove-AzureDisk -DiskName $OSDisk.DiskName
}
When I execute this the call to Remove-AzureVM returns successfully but the call to Remove-AzureDisk returns an error:
Remove-AzureDisk : BadRequest: A disk with name
XXX is currently in use
by virtual machine YYY running within hosted service
ZZZ, deployment XYZ.
Strange thing is, I can issue the same call to Remove-AzureDisk just a few moments later and it returns successfully.
Its as if the call to Remove-AzureVM is returning too quickly. i.e. Its reporting success before the VM has been fully removed, or before the link to the disk has been removed at any rate.
Can anyone explain why this might be and also how I might work around this problem?
thanks in advance
Jamie
What's happening here is that the Disk that is stored in BLOB storage is locked when in use by a VM. You are removing the VM, but it takes a few moments for the Lease on the BLOB to release. That's why you can remove it a few moments later.
There are a few folks who have written PowerShell to break the lease, or you could use PowerShell to use the SDK (or make direct REST API calls) to check lease status.
I ended up writing a script that creates a VM, clones it, then deletes the clones. As part of that I needed to wait until the lease was released hence if you're experiencing this same problem you might want to check my blog post at http://sqlblog.com/blogs/jamie_thomson/archive/2013/11/04/clone-an-azure-vm-using-powershell.aspx as it'll have some code that might help you.
Regards
Jamie
I puzzled at this for quite a while. Ultimately, I found a different command to do what I thought I was doing with this command. I would recommend the remove-azuredatadisk command to delete a disk, as it automatically breaks the lease.
Get-AzureVM -ServiceName <servicename> -name <vmname> |Remove-AzureDataDisk -lun <lun#> -deletevhd | Update-AzureVM
It will spin for a couple of minutes, but it will give you a success/failure output at the end.
This command just does it, and doesn't give you any feedback about which drive was removed. I would recommend tossing in a get-azuredatadisk first just to be sure of what you deleted.
Get-AzureVM -ServiceName <servicename> -name <vmname> | Get-AzureDataDisk
This is related to Windows Azure: Delete disk attached to non-existent VM. Cross-posting my answer here:
I was unable to use the (2016) web portal to delete orphaned disks in my (classic) storage account. Here is a detailed walk-through for deleteing these orphaned disks with PowerShell.
PowerShell
Download and install PowerShell if you haven't already. (Install and configure Azure PowerShell.) Initial steps from this doclink:
Check that the Azure PowerShell module is available after installing:
Get-Module –ListAvailable
If the Azure PowerShell module is not listed, you may need to import it:
Import-Module Azure
Login to Azure Resource Manager:
Login-AzureRmAccount
AzurePublishSettingsFile
Retreive your PublishSettingsFile.
Get-AzurePublishSettingsFile
Get-AzurePublishSettingsFile launches manage.windowsazure.com and prompts you to download an XML file that you can be saved anywhere.
Reference: Get-AzurePublishSettingsFile Documentation
Import-AzurePublishSettingsFile and specify the path to the file just saved.
Import-AzurePublishSettingsFile -PublishSettingsFile '<your file path>'
Show and Remove Disks
Show current disks. (Reference: Azure Storage Cmdlets)
Get-AzureDisk
Quickly remove all disks. (Credit to Mike's answer)
get-azuredisk | Remove-AzureDisk
Or remove disks by name. (Credit to Remove-AzureDisk Documentation)
Remove-AzureDisk -DiskName disk-name-000000000000000000 -DeleteVHD