Is there a way to create a Hyper-V checkpoint with a specified name through WMI? - wmi

I'm trying to find a way to create a Hyper-V checkpoint with a specified name through WMI. I am aware that it is possible to create a snapshot, and then rename it, as seen in this older question, but ideally I would like to create the checkpoint with the name I want to begin with.
This is for Hyper-V for Windows Server 2016. I have already tried setting ElementName to the name I want in the Msvm_VirtualSystemSnapshotSettingData object passed to the CreateSnapshot method, but this doesn't appear to work.

Same problem on my side. I used Msvm_VirtualSystemSnapshotSettingData to set ElementName but it do not set name. Maybe there is some method to rename.
I used this code of PowerShell to create snapshot with name:
$vm = Get-WmiObject -Namespace $Namespace -ComputerName $ComputerName -Query "select * from msvm_computersystem where elementname='$VMName'"
if($vm -eq $null){
throw "Failed to get VM with display name '$VMName'"
}
$cms = Get-WmiObject -Namespace $Namespace -ComputerName $ComputerName -Class Msvm_CollectionManagementService
if($cms -eq $null){
throw "Failed to get instance Msvm_CollectionManagementService"
}
[System.Management.ManagementBaseObject]$result = $cms.DefineCollection($snapshotname, $null, 0)
[int]$res = Job-Observation -WMIJobResult $result -JobAction "Define collection" -JobProgressCaption "Define collection"
if($res -eq 0)
{
$coll = [wmi]$result.DefinedCollection
$coll.Description = $SnapshotDescription
$result = $cms.AddMember($vm, $coll)
$res = Job-Observation -WMIJobResult $result -JobAction "Add collection as member" -JobProgressCaption "Add collection as member"
if($res -eq 0)
{
$csss = Get-WmiObject -Namespace $Namespace -ComputerName $ComputerName -Class Msvm_CollectionSnapshotService
$result = $csss.CreateSnapshot([string]$coll, $null, 32768, $null)
$res = Job-Observation -WMIJobResult $result -JobAction "Creating shapshot" -JobProgressCaption "Create snapshot"
$result = $cms.RenameCollection($snapshotname, [string]$coll)
$res = Job-Observation -WMIJobResult $result -JobAction "Set snaphost name" -JobProgressCaption "Set snapshot name"
}
}
It works fine from PowerShell. But it doesn't work from C++ code.

Got it working!! You can review my full PSscript: ScriptCode Take a look. You need to call ModifySystemSettings method to rename:
$vm = Get-WmiObject -Namespace $Namespace -ComputerName $ComputerName -Query "select * from msvm_computersystem where elementname='$VMName'"
if($vm -eq $null){
throw "Failed to get VM with display name '$VMName'"
}
$Msvm_VirtualSystemSnapshotService = Get-WmiObject -Namespace $Namespace -ComputerName $ComputerName -Class Msvm_VirtualSystemSnapshotService
if($Msvm_VirtualSystemSnapshotService -eq $null)
{
throw "Failed to get Msvm_VirtualSystemSnapshotService instance"
}
[WMI]$Msvm_VirtualSystemSnapshotSettingData = ([WMIClass]"\\.\root\virtualization\v2:Msvm_VirtualSystemSnapshotSettingData").CreateInstance()
if($Msvm_VirtualSystemSnapshotSettingData -eq $null)
{
throw "Failed to get Msvm_VirtualSystemSnapshotSettingData instance"
}
$Msvm_VirtualSystemSnapshotSettingData.ConsistencyLevel = 1
$Msvm_VirtualSystemSnapshotSettingData.IgnoreNonSnapshottableDisks = $true
$Msvm_VirtualSystemSnapshotSettingData.ElementName = $SnapshotName
$Msvm_VirtualSystemSnapshotSettingData.Description = $SnapshotDescription
$Msvm_VirtualSystemSnapshotSettingData
[System.Management.ManagementBaseObject]$result = $Msvm_VirtualSystemSnapshotService.CreateSnapshot(
$vm,
$Msvm_VirtualSystemSnapshotSettingData.GetText(2),
32768)
[int]$res = Job-Observation -WMIJobResult $result -JobAction "Creating snapshot" -JobProgressCaption "Create snapshot"
[WMI]$snapshot = (([WMI]$result.Job).GetRelated("Msvm_VirtualSystemSettingData") | % {$_})
$Msvm_VirtualSystemManagementService = Get-WmiObject -Namespace $Namespace -ComputerName $ComputerName -Class Msvm_VirtualSystemManagementService
if($Msvm_VirtualSystemManagementService -eq $null)
{
throw "Failed to get Msvm_VirtualSystemManagementService instance"
}
$snapshot.ElementName = $SnapshotName
$snapshot.Description = $SnapshotDescription
[System.Management.ManagementBaseObject]$result = $Msvm_VirtualSystemManagementService.ModifySystemSettings($snapshot.GetText(2))
[int]$res = Job-Observation -WMIJobResult $result -JobAction "Renaming snapshot" -JobProgressCaption "Rename snapshot"
$snapshot

Related

Add DOMAIN\Domain Admins group as Full Access to the orphaned Home Directory?

The below script was created by the great https://stackoverflow.com/users/9898643/theo to list all orphaned HomeDirectory:
$ServerHomeDirShare = "\\FileServer\HomeDir$"
$filter = "(Enabled -eq 'true')"
# get all user accounts from AD; only SamAccountName required
$users = Get-ADUser -Filter $filter | Select-Object -ExpandProperty SamAccountName
Get-ChildItem -Path $ServerHomeDirShare -Directory |
Where-Object { $users -notcontains ($_.Name -replace '^(\w+\.\w+).*', '$1') } |
Select-Object -Property Name, FullName,
#{ n = 'LastAccessTime'; e = { $_.LastAccessTime.ToString('yyyy-MM-dd HH:mm:ss') } },
#{ n = "Directory Size (MB)"; e = {
Try {
$Size = (Get-ChildItem -Path $_.FullName -Recurse -ErrorAction Stop |
Measure-Object Length -Sum).Sum / 1MB
[math]::Round($Size, 2)
}
Catch {
"ERROR: $($_.Exception.Message)"
}
}
} |
Export-Csv -NoTypeInformation -Path C:\UserProfilesNotExist-Size.csv
However, there is one more issue that needed fixing, to add DOMAIN\Domain Admins AD group as Full Access to the directory ACL, BUT ONLY when the directory is not accessible or throwing error.
$FullAccessADGroup = "DOMAIN\Domain Admins"
function Take-Ownership
{
param (
[String]$Folder
)
takeown.exe /A /F $Folder
$CurrentACL = Get-Acl $Folder
write-host "`n`t...Adding NT Authority\SYSTEM to $Folder" -ForegroundColor Yellow
$SystemACLPermission = "NT AUTHORITY\SYSTEM", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
$SystemAccessRule = new-object System.Security.AccessControl.FileSystemAccessRule $SystemACLPermission
$CurrentACL.AddAccessRule($SystemAccessRule)
write-host "`t...Adding Infrastructure Services to $Folder" -ForegroundColor Yellow
$AdminACLPermission = $FullAccessADGroup, "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
$SystemAccessRule = new-object System.Security.AccessControl.FileSystemAccessRule $AdminACLPermission
$CurrentACL.AddAccessRule($SystemAccessRule)
Set-Acl -Path $Folder -AclObject $CurrentACL
}
function Test-Folder($FolderToTest) {
$error.Clear()
$ErrorArray = #()
Get-ChildItem $FolderToTest -Recurse -ErrorAction SilentlyContinue | Select-Object FullName
if ($error) {
$ErrorArray = $error + $ErrorArray
foreach ($err in $ErrorArray) {
if ($err.FullyQualifiedErrorId -eq "DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand") {
Write-Host Unable to access $err.TargetObject -ForegroundColor Red
Write-Host Attempting to take ownership of $err.TargetObject -ForegroundColor Yellow
Take-Ownership($err.TargetObject)
Test-Folder($err.TargetObject)
}
}
}
}
Test-Folder $source
Because even though I am using DOMAIN\Administrator account to execute the script above, I cannot get the directory size or even opened the directory via the UNCPath, this is the error:
ERROR: Access to the path '\\FileServer\HomeDir$\Jane.Liz.V2' is denied.
ERROR: Access to the path '\\FileServer\HomeDir$\Lisa.Chan.V5' is denied.
ERROR: Access to the path '\\FileServer\HomeDir$\Carolline.Marce.V6' is denied.
...

Iterate through PSOObject

I am writing a script where Block Devices attached to a EC2 instance are matched and then a corresponding tag is set.
I have following PSOObject and visualization in the console
New-Object PSObject -Property #{
Disk = $Disk;
Partitions = $Partitions;
DriveLetter = If ($DriveLetter -eq $null) { "N/A" } Else { $DriveLetter };
EbsVolumeId = If ($EbsVolumeID -eq $null) { "N/A" } Else { $EbsVolumeID };
Device = If ($BlockDeviceName -eq $null) { "N/A" } Else { $BlockDeviceName };
VirtualDevice = If ($VirtualDevice -eq $null) { "N/A" } Else { $VirtualDevice };
VolumeName = If ($VolumeName -eq $null) { "N/A" } Else { $VolumeName };
}
} | Sort-Object Disk | Format-Table -AutoSize -Property Disk, Partitions, DriveLetter, EbsVolumeId, Device, VirtualDevice, VolumeName
Now I have following simple New-EC2tag command tagging the instance.
New-EC2Tag -Resource $InstanceId -Tags #{ Key = $DriveLetter; Value = $BlockDeviceName}
Basically it just writes the C: Drive and I believe I have to iterate through the PSObject to basically also set the corresponding drives.
I would bge grateful for any help.

PowerShell WebServiceProxy with forms authentication

I am working on building a few interfaces with Remedy 9.1 web services. It is configured with Forms authentication to get to the WSDL. I would like to keep it in that configuration so that the more powerful web services remain protected.
I have parts of a solution, but I am not sure that they can work together, perhaps you know of a solution?
This works if I remove forms auth:
function New-ObjectFromProxy {
param($proxy, $proxyAttributeName, $typeName)
# Locate the assembly for $proxy
$attribute = $proxy | gm | where { $_.Name -eq $proxyAttributeName }
$str = "`$assembly = [" + $attribute.TypeName + "].assembly"
invoke-expression $str
# Instantiate an AuthenticationHeaderValue object.
$type = $assembly.getTypes() | where { $_.Name -eq $typeName }
return $assembly.CreateInstance($type)
}
$Now = get-date -Format G
$Q = "'System Broadcast End Date' >= """ + $Now + """"
$proxy = New-WebServiceProxy -Uri "https://mycompany-itsm.columncloud.com/arsys/WSDL/public/servername/CFG%3ABroadcast"
$authHeader = New-ObjectFromProxy -proxy $proxy -proxyAttributeName "AuthenticationInfoValue" -typeName "AuthenticationInfo"
$authHeader.userName = "username"
$authHeader.password = "password"
$proxy.AuthenticationInfoValue = $authHeader
$Response = $proxy.GetList($Q,"","")
$Response | format-Table Broadcast_Start_Date, Broadcast_Message
However, if I move the webservice back behind the forms auth, I can get to the WSDL if I do this:
#this is the url that you want will send thae request to
$url = "https://mycompany-itsm.columncloud.com/arsys/servlet/LoginServlet"
#here you can set your POST params
$parameters = "username=username&pwd=ppaasswwoorrdd&encpwd=1&ipoverride=0&initialState=-1&timezone=-28800000&goto=/arsys/WSDL/protected/servername/HPD_IncidentInterface_Create_WS"
#creating the xmlHtpp system object
$http_request = New-Object -ComObject Msxml2.XMLHTTP
$http_request.open('POST', $url, $false)
#Setting required header of the request
$http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
$http_request.setRequestHeader("Content-length", $parameters.length)
#Assigning the params to the request
$Resp = $http_request.send($parameters)
echo $http_request.responseText
I would like to find a solution to combine these solutions together to use forms auth to get to the WSDL and create a webServiceProxy object. Perhaps my google-fu is weak, but I have not found a formsauth solution for new-webserviceProxy.
Origin code from POSHCODE site. updated to add formsauth
Basically I had to use the way back machine to figure out how this was done before it was made super simple by new-WebServiceProxy. I fire up a web request to post with forms auth data and grab the cookie for the conversation. If my cookie expires, I go get a new one (might explain my waistline).
Huge props to Oisin Grehan for publishing his code 7 years ago.
Call it like this:
.\New-WebServiceProxy-FormsAuth.ps1 -Url "https://mycompany/WSDL/public/servername/CFG%3ABroadcast" -Namespace "mystuff" -Cookies $CookieJar -lurl "https://mycompany/servlet/LoginServlet" -postData "username=username&pwd=ppaasswwoorrdd&encpwd=1&ipoverride=0&initialState=-1&timezone=-28800000"
# New-WebServiceProxy-FormsAuth.ps1 (v3.0 Sep 23, 2009)
#
# Oisin Grehan <oising#gmail.com> (x0n)
# ghangas
#
# Usage:
# $proxy = .\New-WebServiceProxy.ps1 [-Url] http://site/service.asmx -lurl <http://site/loginpostpage> -postData <form data url encoded> [[-SoapProtocol] <Soap | Soap12>] [-Namespace <namespace>] [-Cookies <CookieContainer>]
#
# to see available webmethods:
# $proxy | gm
#
param($url = $(throw "need `$url"), [string]$protocol = "Soap", [string]$Namespace="", [System.Net.CookieContainer]$CookieJar, [string]$lurl, [string]$postData)
[void][system.Reflection.Assembly]::LoadWithPartialName("system.web.services")
trap {
"Error:`n`n $error";
break;
}
#$request = [System.Net.WebRequest]::Create($url);
$dcp = new-object system.web.services.discovery.discoveryclientprotocol
if ($CookieJar -ne $null) {
If ($CookieJar.ToString() = "System.Net.CookieContainer") {
$dcp.CookieContainer = $CookieJar
}
}
Write-Progress "Discovery" "Searching..."
$dcp.AllowAutoRedirect = $true
try {[void]$dcp.DiscoverAny($url)
}
catch {
$CookieJar = New-Object System.Net.CookieContainer
$buffer = [text.encoding]::ascii.getbytes($postData)
[net.httpWebRequest] $req = [net.webRequest]::create($lurl)
$req.method = "POST"
$req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
$req.Headers.Add("Accept-Language: en-US")
$req.Headers.Add("Accept-Encoding: gzip,deflate")
$req.Headers.Add("Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7")
$req.AllowAutoRedirect = $true
$req.UserAgent = "Mozilla/4.0"
$req.ContentType = "application/x-www-form-urlencoded"
$req.ContentLength = $buffer.length
$req.TimeOut = 50000
$req.KeepAlive = $true
$req.CookieContainer = $CookieJar
$reqst = $req.getRequestStream()
$reqst.write($buffer, 0, $buffer.length)
$reqst.flush()
$reqst.close()
[net.httpWebResponse] $res = $req.getResponse()
$dcp.CookieContainer = $CookieJar
[void]$dcp.DiscoverAny($url)
}
$dcp.ResolveAll()
# get service name
foreach ($entry in $dcp.Documents.GetEnumerator()) { # needed for Dictionary
if ($entry.Value -is [System.Web.Services.Description.ServiceDescription]) {
$script:serviceName = $entry.Value.Services[0].Name
Write-Verbose "Service: $serviceName"
}
}
Write-Progress "WS-I Basic Profile 1.1" "Validating..."
$ns = new-Object System.CodeDom.CodeNamespace $Namespace
$wref = new-object System.Web.Services.Description.WebReference $dcp.Documents, $ns
$wrefs = new-object system.web.services.description.webreferencecollection
[void]$wrefs.Add($wref)
$ccUnit = new-object System.CodeDom.CodeCompileUnit
[void]$ccUnit.Namespaces.Add($ns)
$violations = new-object system.web.Services.Description.BasicProfileViolationCollection
$wsi11 = [system.web.services.WsiProfiles]::BasicProfile1_1
if ([system.web.Services.Description.WebServicesInteroperability]::CheckConformance($wsi11, $wref, $violations)) {
Write-Progress "Proxy Generation" "Compiling..."
$webRefOpts = new-object System.Web.Services.Description.WebReferenceOptions
$webRefOpts.CodeGenerationOptions = "GenerateNewAsync","GenerateProperties" #,"GenerateOldAsync"
#StringCollection strings = ServiceDescriptionImporter.GenerateWebReferences(
# webReferences, codeProvider, codeCompileUnit, parameters.GetWebReferenceOptions());
$csprovider = new-object Microsoft.CSharp.CSharpCodeProvider
$warnings = [System.Web.Services.Description.ServiceDescriptionImporter]::GenerateWebReferences(
$wrefs, $csprovider, $ccunit, $webRefOpts)
if ($warnings.Count -eq 0) {
$param = new-object system.CodeDom.Compiler.CompilerParameters
[void]$param.ReferencedAssemblies.Add("System.Xml.dll")
[void]$param.ReferencedAssemblies.Add("System.Web.Services.dll")
$param.GenerateInMemory = $true;
#$param.TempFiles = (new-object System.CodeDom.Compiler.TempFileCollection "c:\temp", $true)
$param.GenerateExecutable = $false;
#$param.OutputAssembly = "$($ns.Name)_$($sdname).dll"
$param.TreatWarningsAsErrors = $false;
$param.WarningLevel = 4;
# do it
$compileResults = $csprovider.CompileAssemblyFromDom($param, $ccUnit);
if ($compileResults.Errors.Count -gt 0) {
Write-Progress "Proxy Generation" "Failed."
foreach ($output in $compileResults.Output) { write-host $output }
foreach ($err in $compileResults.Errors) { write-warning $err }
} else {
$assembly = $compileResults.CompiledAssembly
if ($assembly) {
if ($namespace) {
$serviceType = $assembly.GetType($namespace + "." + $serviceName)
} else {
$serviceType = $assembly.GetType($serviceName)
}
$assembly.GetTypes() | % { Write-Verbose $_.FullName }
} else {
Write-Warning "Failed: `$assembly is null"
return
}
# return proxy instance
$proxy = new-object $serviceType.FullName
$proxy # dump instance to pipeline
}
} else {
Write-Progress "Proxy Generation" "Failed."
Write-Warning $warnings
}
#Write-Progress -Completed
}

Can't check ArchiveStatus for mailbox

I am building a script that'll check if ArchiveStatus for an Office 365 mailbox is active or not. I am able to pull the data, but my if/else statement doesn't seem to work. This is what I have so far:
$Data = Get-Mailbox -Identity "MailboxName" | ft name,*Archive*
$Data
if ($_.ArchiveStatus -eq $true) {
Write-Host "Archive Enabled"
} else{
Write-Host "Archiving Disabled"
}
No matter what mailbox I search up, the result is always "Archiving Disabled" even if the console shows user enabled.
The current object variable ($_) is only available in a pipeline context. Your if statement is outside a pipeline, so $_ doesn't exist, meaning that $_.ArchiveStatus evaluates to $null, which is interpreted as $false in the comparison.
Change this:
$Data = Get-Mailbox -Identity "MailboxName" | ft name,*Archive*
$Data
if ($_.ArchiveStatus -eq $true) {
Write-Host "Archive Enabled"
} else {
Write-Host "Archiving Disabled"
}
into this:
$Data = Get-Mailbox -Identity "MailboxName"
$Data | ft name,*Archive*
if ($Data.ArchiveStatus -eq $true) {
Write-Host "Archive Enabled"
} else {
Write-Host "Archiving Disabled"
}
and the problem should disappear.
So I was able to figure it out with help from #Ansgar Wiechers. The -eq $true wasn't working because it was either Active or None. When i created the variable for this it worked. The working code is below:
$Data = Get-Mailbox -Identity "MailboxName"
$Data | ft name, ArchiveStatus
$Status = "Active"
if($Data.ArchiveStatus -eq $Status) {
Write-Host "Archive Enabled"
} Else {
Write-Host "Archiving Disabled"
}
Thank you for your help!!

Create Active Directory users with certain attributes in a script

I'm working on a PowerShell script to make it easier to create Active Directory users including some attributes. For now I need to process the "Manager" field. We have 4 departments, 4 managers. I want to create this using logic like "if department is 1 of the 4 departments then give 1 of the 4 managers."
How can I process this?
Import-Module activedirectory
#Store the data from ADUsers.csv in the $ADUsers variable
$ADUsers = Import-csv "c:\temp\scripts\ADUsers.csv"
#Loop through each row containing user details in the CSV file
foreach ($User in $ADUsers)
{
#Read user data from each field in each row and assign the data to a variable as below
$Firstname = $User.firstname
$Lastname = $User.lastname
$Username = $User.username
$Password = $User.password
$OU = $User.ou #This field refers to the OU the user account is to be created in
$userprincipalname = $user.userprincipalname
$displayname = $user.displayname
$title = $user.jobtitle
$department = $user.department
#Check to see if the user already exists in AD
if (Get-ADUser -F {SamAccountName -eq $Username})
{
#If user does exist, give a warning
Write-Warning "A user account with username $Username already exist in Active Directory."
}
else
{
Write-host
#User does not exist then proceed to create the new user account
#Account will be created in the OU provided by the $OU variable read from the CSV file
New-ADUser -Name $displayname -SamAccountName $Username -UserPrincipalName $userprincipalname -GivenName $Firstname -Surname $Lastname -Enabled $True -DisplayName $displayname -AccountPassword (convertto-securestring $Password -AsPlainText -Force)
}
} #end function
Something like this?
if ($department -eq "A")
{
$manager = "ManagerA"
}
else if ($department -eq "B")
{
$manager = "ManagerB"
}
else if ($department -eq "C")
{
$manager = "ManagerC"
}
else if ($department -eq "D")
{
$manager = "ManagerD"
}
New-ADUser -Name $displayname -SamAccountName $Username -UserPrincipalName $userprincipalname -GivenName $Firstname -Surname $Lastname -Enabled $True -DisplayName $displayname -AccountPassword (convertto-securestring $Password -AsPlainText -Force) -Manager $manager
"ManagerA/B/C/D" are SAM account name of user objects of managers.
I have solved the problem with a Switch like below.
Switch ($department) {
"dept 1" {$manager = "cn=jim smith,ou=west,dc=domain,dc=com"}
"dept 2" {$manager = "cn=sally wilson,ou=east,dc=domain,dc=com"}
"dept 3" {$manager = "cn=frank johnson,ou=east,dc=domain,dc=com"}
"dept 4" {$manager = "cn=mary tutle,ou=west,dc=domain,dc=com"}