Add and Remove Snapshots with PowerCLI for multiple servers - virtual-machine

I am trying to get a snapshot from multiple servers with PowerCLI.
Connect-VIServer -server 192.168.0.1 -user acconut -password xxx
$vmlist = Get-Content C:\Users\Desktop\Test\Servers.txt
foreach($VM in $VMlist) {
New-Snapshot -VM $vm -Name Temp-SnapShot -description (get-date),'Created for patching'
}
Disconnect-VIServer -Confirm:$false
If I delete get-date, the script will work. But I need to type date in descriptions. How should I change the Script above to have Get-Date in snapshot's descriptions?
Also, I need to delete these snapshot after a couple days:
Connect-VIServer -server 192.168.0.1 -user acconut -password xxx
$vmlist = Get-Content C:\Users\Desktop\Test\Servers.txt
foreach($VM in $VMlist) {
Remove-Snapshot -VM $vm -snapshot -confirm:$false
}
Disconnect-VIServer -Confirm:$false
I could not delete snapshot with Remove-Snapshot because I get this error:
Remove-Snapshot : Missing an argument for parameter 'Snapshot'. Specify a parameter of type 'VMware.VimAutomation.ViCore.Types.V1.VM.Snapshot[]' and try again.
Thank you for your help.

On the description part you can put $date = get-date and do -description $date. That should work.
before you can remove the snapshot you need to get the snapshot. I would say edit your remove-snapshot line to include this:
Get-Snapshot -VM $vm | Remove-Snapshot -confirm:$false
you might even want to add -RemoveChildren:$true ( this will remove "All" snapshots )

This should help with what you are looking for. It finds the snaps and removes any greater than 10 days old.
$snaps = Get-VM | Get-snapshot | Select vm,name,created
if ($snaps.created -le (get-date).adddays(-10))
{
$remsnaps = $snaps | Where {$_.created -le (get-date).adddays(-10)}
$remsnaps | Out-File "C:\Scripts\RemoveSnaps\logs\remsnapsVC.txt"
$remobject = Get-Content "C:\Scripts\RemoveSnaps\logs\remsnapsVC.txt"
$rmsnap = Get-Snapshot $remsnaps.vm
Remove-Snapshot -Snapshot $rmsnap -RemoveChildren -Confirm:$false #-WhatIf
$MessageSubject = "The following snaps were removed from NTVCenter01!"
$MessageBody = $remobject | fl | out-string
SendEmail
}

Related

VM power state not updated and returned correctly

I'm trying to power on VMs which I previously powered off using PowerCLI.
When I try to run the following script (part of a bigger one) I still get a status which is not "PoweredOn", even though I can see on the VSphere console that the machine was powered on.
I get this also in other situations and I try to re-get the virtual machines, but I fail to make this work.
If I don't re-get the VMs, I sometimes get error claiming the VM I'm referring to is null.
What am I doing wrong? What am I missing?
Here are the script lines:
$VMs = get-vm | Where-object {($_.Name -like $vmNamePatternToSearch)}# | Out-Null
foreach ($vm in $VMs) {
#$vm = Get-VM -Name $vm.Name #| Out-Null
if ($vm.powerstate -ieq "poweredoff") {
Start-VM -VM $vm -Confirm:$False | Out-Null
Write-Host -NoNewline 'Powering On' $vm.Name.ToString().PadRight(22)
do {
Start-Sleep -Seconds 1
Write-Host -NoNewline '|' $vm.powerstate
} until ($vm.powerstate -ieq "PoweredOn")
Write-Host
}
}
So my output is "| PoweredOff| PoweredOff| PoweredOff| PoweredOff| PoweredOff|..."
Even though the machine is already up.
Even if I un-comment the "#$vm = Get-VM -Name $vm.Name #| Out-Null" line - still no go.
I would appreciate your input.
Thanks!
PowerShell's objects are point in time references. So your vm variable will continue to reflect the status of the VM at the time you ran the get-vm cmdlet.
To help overcome this you could run something like the following to reference the updated state of the VM during your loop:
do {
Start-Sleep -Seconds 1
Write-Host -NoNewline '|' $vm.powerstate
} until ((Get-VM $vm).powerstate -ieq "PoweredOn")

Using PS to query SQL for list of users, then disable Active Directory accounts?

I'm trying to use Powershell to query SQL database for a list of suspended users, pipe into a variable, then use that to loop through and disable those AD accounts. Here's the code I'm using... note I'm just trying to write the output now instead of making a change so I don't do anything I regret.
Import-Module ActiveDirectory
$Users = Invoke-Sqlcmd -ServerInstance 'SERVER' -Database 'NAME' -Query "SELECT EmployeeID,
EmployeeStatus FROM [NAME].[dbo].[employee] WHERE EmployeeStatus = 'S'"
foreach ($user in $users)
{
Get-ADUser -Filter "EmployeeID -eq '$($user.EmployeeID)'" `
-SearchBase "OU=Logins,DC=domain,DC=com" |
#Set-ADUser -Identity $Name -Enabled $False
Write-Verbose $User
}
The SQL query is working fine, but when I run the loop it's giving this error:
Write-Verbose : The input object cannot be bound to any parameters for
the command either because the
command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline
input.
Am I just formatting this incorrectly? Or is there another way I should be thinking of this?
Thanks in advance!
If you would like to find inactive user accounts in Active Directory, you can use the Search-ADAccount cmdlet. You need to do this use the “-AccountInActive” parameter with Search-ADAccount.
PowerShell command below:
Search-ADAccount –AccountInActive –TimeSpan 120:00:00:00 –ResultPageSize 2000 –ResultSetSize $null | ?{$_.Enabled –eq $True} | Select-Object Name, SamAccountName, DistinguishedName | Export-CSV “C:\Temp\InActiveADUsers.CSV” –NoTypeInformation
I have given timespan for 120days and export the list into csv file.

Move files with todays modified date to other folder then delete files older then two weeks

Script needs to be run every one week (via task scheduler) and achieve the following:
When the script runs, all files on the date of script run (modified date for files) in certain folder needs to be copied to other folder, when copied, delete everything older 2 weeks from the original folder.
See screenshot for more explanation
I have something with powershell:
$path = "C:\FromFTP\*.*"
$Destination = "C:\Backup"
Foreach($file in (Get-ChildItem $path))
{
If($file.LastWriteTime -gt (Get-Date).adddays(-1).date)
{
Move-Item -Path $file.fullname -Destination $Destination
}
}
But maybe it could be also with vba..
Can someone help me with that? Thank you!
Try this:
$Path = "C:\FromFTP";
$Destination = "C:\Backup";
$Today = (Get-Date).Date;
Get-ChildItem -Path $Path |
Where-Object { ($_.LastWriteTime -ge $Today) -and ($_.LastWriteTime -lt $Today.AddDays(1)) } |
Move-Item -Destination $Destination;
Get-ChildItem -Path $Path |
Where-Object { $_.CreationTime -lt $Today.AddDays(-14) } |
Remove-Item;
The first line gets every file that was last written to (LastWriteTime) sometime between midnight today an before midnight tomorrow. Obviously it's difficult to write files tomorrow, but it makes it easy to run the script for a date in the past, too.
The second line deleted every file that was first created (CreationTime) before 14 days before today. The number of days might be off by one, depending on how you count.

Powershell Job within SQL Server Agent

I am having trouble with a powershell job returning a result set when it runs, it runs successfully but no results. I am running it against a list of servers. If I run the script against a specific server that I know I can connect to, it runs fine and gives results.
This is a script within the sql server agent running powershell. Am I using the SMO object right? Ive tried to use a try/catch (job fails), ive tried to add -ErrorAction "Continue" to the script (job fails), using smo to resolve a server name in order to use the if statement (job succeeds) but no results. Here is the script:
$ErrorActionPreference = "Continue";
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
$instanceNameList = Get-Content "c:\Scripts\InPutFiles\servers.txt";
$results = #()
foreach($instanceName in $instanceNameList)
{
$serverObject = New-Object Microsoft.SqlServer.Management.Smo.Server($instanceName)
$serverName = $serverObject.ComputerNamePhysicalNetBIOS;
if($serverName -ne $null) {
$results += Invoke-Sqlcmd -Query "
(My Query is in here!)
" -ServerInstance $instanceName.Name}
$instanceName| Where-Object {$_} | Export-csv 'C:\scripts\HungJobs_UnabletoConnect.csv' -NoTypeInformation
}
$results| Where-Object {$_} | Export-csv 'C:\scripts\HungJobs.csv' -NoTypeInformation
Mid-way through your script, is the $scripts variable being populated properly by the Invoke-Sqlcmd?
If so, I'm thinking that you should be able to do away with Where-Object {$_} on your last two commands. Where-Object is used as a filter, and in these two cases it doesn't seem to be filtering anything. You should just be able to directly export the $instanceName and $results directly to csv.

How do I get the value of a registry key and ONLY the value using powershell

Can anyone help me pull the value of a registry key and place it into a variable in PowerShell? So far I have used Get-ItemProperty and reg query and although both will pull the value, both also add extra text. I need just the string text from the registry key and ONLY the string text from the key. I'm sure I could create a function to strip off the extra text but if something changes (i.e. reg key name) it might affect this.
$key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
(Get-ItemProperty -Path $key -Name ProgramFilesDir).ProgramFilesDir
I've never liked how this was provider was implemented like this : /
Basically, it makes every registry value a PSCustomObject object with PsPath, PsParentPath, PsChildname, PSDrive and PSProvider properties and then a property for its actual value. So even though you asked for the item by name, to get its value you have to use the name once more.
NONE of these answers work for situations where the value name contains spaces, dots, or other characters that are reserved in PowerShell. In that case you have to wrap the name in double quotes as per http://blog.danskingdom.com/accessing-powershell-variables-with-periods-in-their-name/ - for example:
PS> Get-ItemProperty Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7
14.0 : C:\Program Files (x86)\Microsoft Visual Studio 14.0\
12.0 : C:\Program Files (x86)\Microsoft Visual Studio 12.0\
11.0 : C:\Program Files (x86)\Microsoft Visual Studio 11.0\
15.0 : C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\V
S7
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS
PSChildName : VS7
PSProvider : Microsoft.PowerShell.Core\Registry
If you want to access any of the 14.0, 12.0, 11.0, 15.0 values, the solution from the accepted answer will not work - you will get no output:
PS> (Get-ItemProperty Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7 -Name 15.0).15.0
PS>
What does work is quoting the value name, which you should probably be doing anyway for safety:
PS> (Get-ItemProperty "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7" -Name "15.0")."15.0"
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\
PS>
Thus, the accepted answer should be modified as such:
PS> $key = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7"
PS> $value = "15.0"
PS> (Get-ItemProperty -Path $key -Name $value).$value
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\
PS>
This works in PowerShell 2.0 through 5.0 (although you should probably be using Get-ItemPropertyValue in v5).
Harry Martyrossian mentions in a comment on his own answer that the
Get-ItemPropertyValue cmdlet was introduced in Powershell v5, which solves the problem:
PS> Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion' 'ProgramFilesDir'
C:\Program Files
Alternatives for PowerShell v4-:
Here's an attempt to retain the efficiency while eliminating the need for repetition of the value name, which, however, is still a little cumbersome:
& {
(Get-ItemProperty `
-LiteralPath HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion `
-Name $args
).$args
} ProgramFilesDir
By using a script block, the value name can be passed in once as a parameter, and the parameter variable ($args) can then simply be used twice inside the block.
Alternatively, a simple helper function can ease the pain:
function Get-RegValue([String] $KeyPath, [String] $ValueName) {
(Get-ItemProperty -LiteralPath $KeyPath -Name $ValueName).$ValueName
}
Note: All solutions above bypass the problem described in Ian Kemp's's answer - the need to use explicit quoting for certain value names when used as property names; e.g., .'15.0' - because the value names are passed as parameters and property access happens via a variable; e.g., .$ValueName
As for the other answers:
Andy Arismendi's helpful answer explains the annoyance with having to repeat the value name in order to get the value data efficiently.
M Jeremy Carter's helpful answer is more convenient, but can be a performance pitfall for keys with a large number of values, because an object with a large number of properties must be constructed.
I'm not sure if this has been changed, or if it has something to do with which version of PS you're using, but using Andy's example, I can remove the -Name parameter and I still get the value of the reg item:
PS C:\> $key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
PS C:\> (Get-ItemProperty -Path $key).ProgramFilesDir
C:\Program Files
PS C:\> $psversiontable.psversion
Major Minor Build Revision
----- ----- ----- --------
2 0 -1 -1
Given a key \SQL with two properties:
I'd grab the "MSSQLSERVER" one with the following in-cases where I wasn't sure what the property name was going to be to use dot-notation:
$regkey_property_name = 'MSSQLSERVER'
$regkey = get-item -Path 'HKLM:\Software\Microsoft\Microsoft SQL Server\Instance Names\SQL'
$regkey.GetValue($regkey_property_name)
Well you need to be specific here. As far as I know, the key in a registry is a "folder" of properties. So did you mean get the value of a property? If so, try something like this:
(Get-ItemProperty HKLM:\Software\Microsoft\PowerShell\1\PowerShellEngine -Name PowerShellVersion).PowerShellVersion
First we get an object containing the property we need with Get-ItemProperty and then we get the value of for the property we need from that object. That will return the value of the property as a string. The example above gives you the PS version for "legacy"/compatibility-mdoe powershell (1.0 or 2.0).
Following code will enumerate all values for a certain Registry key, will sort them and will return value name : value pairs separated by colon (:):
$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework';
Get-Item -Path $path | Select-Object -ExpandProperty Property | Sort | % {
$command = [String]::Format('(Get-ItemProperty -Path "{0}" -Name "{1}")."{1}"', $path, $_);
$value = Invoke-Expression -Command $command;
$_ + ' : ' + $value; };
Like this:
DbgJITDebugLaunchSetting : 16
DbgManagedDebugger : "C:\Windows\system32\vsjitdebugger.exe" PID %d APPDOM %d EXTEXT "%s" EVTHDL %d
InstallRoot : C:\Windows\Microsoft.NET\Framework\
Not sure at what version this capability arrived, but you can use something like this to return all the properties of multiple child registry entries in an array:
$InstalledSoftware = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" | ForEach-Object {Get-ItemProperty "Registry::$_"}
Only adding this as Google brought me here for a relevant reason and I eventually came up with the above one-liner for dredging the registry.
If you create an object, you get a more readable output and also gain an object with properties you can access:
$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework'
$obj = New-Object -TypeName psobject
Get-Item -Path $path | Select-Object -ExpandProperty Property | Sort | % {
$command = [String]::Format('(Get-ItemProperty -Path "{0}" -Name "{1}")."{1}"', $path, $_)
$value = Invoke-Expression -Command $command
$obj | Add-Member -MemberType NoteProperty -Name $_ -Value $value}
Write-Output $obj | fl
Sample output:
InstallRoot : C:\Windows\Microsoft.NET\Framework\
And the object:
$obj.InstallRoot = C:\Windows\Microsoft.NET\Framework\
The truth of the matter is this is way more complicated than it needs to be. Here is a much better example, and much simpler:
$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework'
$objReg = Get-ItemProperty -Path $path | Select -Property *
$objReg is now a custom object where each registry entry is a property name. You can view the formatted list via:
write-output $objReg
InstallRoot : C:\Windows\Microsoft.NET\Framework\
DbgManagedDebugger : "C:\windows\system32\vsjitdebugger.exe"
And you have access to the object itself:
$objReg.InstallRoot
C:\Windows\Microsoft.NET\Framework\