How to delete previous database snapshot using regex? - powershell

We are restoring a database called Cube1 as "snapshots" using below command.
$CUBE = "$CUBE-Snapshot-$dateStamp"
Restore-ASDatabase -Server $Target_Server -RestoreFile $BFile -Name $CUBE -Security:$SecurityChoice -AllowOverwrite -ErrorAction Stop
However, this is supposed to run weekly indefinitely and our server memory capacity will be diminished leading to performance issues eventually, which we don't want.
Therefore, I'd like to delete existing snapshots, so ultimate result would be something like this (1 snapshot only weekly):
I tried the following:
Import-Module sqlserver
$AnalysisServer = New-Object Microsoft.AnalysisServices.Server
$AnalysisServer.connect("$Target_Server")
$AnalysisServer.Databases["*$CUBE-Snapshot*"].Drop()
and the drop operation failed.
You cannot call a method on a null-valued expression.
When specifying manually the snapshot name:
$AnalysisServer.Databases["Cube1-Snapshot-05-30-2021-0314PM"].Drop()
The drop operation succeeds.
I suppose I need to use some sort of regex then since wildcards didn't seem to work for the name, so how do I accomplish that?

Using [...] to index into the .DataBases collections corresponds to the parameterized .Item[] property, which supports (a) targeting a database by numerical index or (b) by verbatim name - using patterns to match database names is not supported.
Therefore, you need to filter the database collection explicitly, which you can do with the .Where() array method, combined with member-access enumeration, which allows you to call the .Drop() method on every matching database:
$AnalysisServer.Databases.Where({ $_.Name -like "*$CUBE-Snapshot*" }).Drop()
Note: If no databases match, the above will generate a statement-terminating error; to avoid that, use a two-step approach:
$matchingDbs = $AnalysisServer.Databases.Where({ $_.Name -like "*$CUBE-Snapshot*" })
if ($matchingDbs) { $matchingDbs.Drop() }
To delete all snapshots except the most recent one, based on the naming convention shown in the question:
$matchingDbs = $AnalysisServer.Databases.Where({ $_.Name -like "*$CUBE-Snapshot*" })
if ($matchingDbs.Count -ge 2) {
($matchingDbs | Sort-Object Name -Descending | Select-Object -Skip 1).Drop()
}

Related

PowerShell script to apply next available number to a user

Total noob here and i have a dilema... I need to apply Microsoft Calling Plan numbers to users based on region. Now what i want to do is import a .csv file of all the users and have powershell run a command that looks up available calling plan numbers in that region and then assign one to a user. then onto the next user, then the next and so on using the next available number each time.
As i have said i am not great with opwershell and i have thrown this togeth.
$Users = Import-CSV c:\filelocation\users.csv
$loc= (Get-CsOnlineLisLocation -City <city>)
$usernumber = (Get-CsPhoneNumberAssignment -isocountrycode GB -LocationId $loc.LocationId -NumberType CallingPlan -CapabilitiesContain UserAssignment -PstnAssignmentStatus Unassigned)
Foreach($user in $users)
{
Set-CsPhoneNumberAssignment -Identity $_.UPN -PhoneNumber $usernumber -PhoneNumberType CallingPlan
}
I have recently been scolded for using back ticks so that is something i need to ammend here but what i want it to do is lookup unassigned calling plan numbers for the $usernumber parameter and apply it in the set-CsPhoneNumberAssignment.
I have no idea how i loop it to apply the first available number and then move onto the next..
please help.
This script has not yet been run but i dont think it will work.
For matched loops like this you can use regular for($index = 0; $index -le $users.Length; $index++) {} syntax.
Then pass the index to both $Users and $UserNumber lists: $Users[$index].
Check for appropriate sizes first if necessary (especially if there are more users than there are available plans) to avoid ArrayOutOfBoundsExceptions.

Why is Enumeration Name property not working?

I have a script that i want to display the connection strings of a database
Import-Module SqlServer
$AS = New-Object Microsoft.AnalysisServices.Server
$AS.connect("server1")
Now if i use the FindByName() property
$db = $AS.Databases.FindByName("database1")
$db.DataSources[0].ConnectionString
I get back the connection string successfully
however if i use Enumerator
foreach ($db in $AS.Databases.GetEnumerator())
{ $dbName = $db.Name
$dbName
$dbName.DataSources[0].ConnectionString
}
I get back the database name along with error/exception (because it couldnt get connection string for some reason):
database1
Cannot index into a null array.
database2
Cannot index into a null array.
I tried the following also:
$database1 = "database1"
$database1.DataSources[0].ConnectionString
and i also get back the same exception
So why is it that only FindByName works?
for additional info, this is what GetEnumerator lists:
$AS.Databases.GetEnumerator()
but also $AS.Databases
outputs the same thing...so whats even the point of the enumerator?
gm -i $AS.Databases
gm -i $AS.Databases..GetEnumerator()
Part of what you're seeing is PowerShell's handling of (some) enumerables. Many (most?) are unrolled by PowerShell automatically, so the call to .GetEnumerator() isn't needed.
That's what's happening in your last example, looking at $AS.Databases vs $AS.Databases.GetEnumerator(). But it's only because you sent it out to the pipeline in that case; it's the display process that did the unrolling (in both cases).
If you did gm -i $AS.Databases vs gm -i $AS.Databases.GetEnumerator() you're going to see the difference; same if you assigned each of those to a variable and tried to call methods on them.
But back to using foreach it should again be redundant: foreach ($db in $AS.Databases) should work the same as foreach ($db in $AS.Databases.GetEnumerator()) but I don't have this type in my env right now to test that.
So back to the issue at hand inside the foreach, I suggest you start checking types again. Compare:
$db = $Analysis_Server.Databases.FindByName("database1")
gm -i $db
to
foreach ($db in $AS.Databases.GetEnumerator())
{
gm -i $db
break
}
You might find the types aren't what you think.
This is especially true because you're using dot . notation, because PowerShell has another array shortcut built-in since version 3, whereby you can use . on an array of types, to return an array of the .Property of each item. For example:
$p = Get-Process chrome # choose your own adventure
$p.Count
$p[0].Name
$p.Name
So a property you thought you were accessing on a single object, may have been on an array of objects, and may have been returning an array (or a single object), and handing that in the foreach may have returned a different quantity, or something, resulting in your attempt to index into what used to be an array, no longer work.
But again it's speculation on my part because I don't have those objects. Hopefully this helps you dig deeper into it though.
PowerShell does its own enumerating.
This done the trick!
foreach ($db in $AS.Databases){
Write-Hst $db.Name -Fore green
$db.DataSources | ForEach-Object{$_.ConnectionString}
}

How do i return compatibility level of just one database?

I referenced this page: https://technet.microsoft.com/en-us/hh213141(v=sql.100)
Import-Module SqlServer
$as = New-Object Microsoft.AnalysisServices.Server
$as.connect("$Server")
$as.databases
Write-Host "Compatibility level ="$as.DefaultCompatibilityLevel
but this returns ALL databases back...
I want to specify just one database to get the compatibility level of...
I tried this,
$as.databases["$Database"]
the DB i am interested in has lvl 1103,
but it seems not to return the proper level, because its returning 1200...
I am not familiar with this API, but I see that the $as.Databases property is of type DatabaseCollection. The DatabaseCollection class does have indexers that take either an Int32 or a String, like you're attempting to use with $as.databases["$Database"].
Note that the documentation for the String indexer says the parameter is an "identifier" for the database to be returned. Also note that the Database class has separate properties for ID and Name, so there is a distinction between the two. So, my suggestion is, make sure you are passing the ID, not the name, when you try to retrieve a Database instance that way.
Alternatively, if you do want to search by name you could use the FindByName method...
$as.Databases.FindByName($Database)
...or GetByName method...
$as.Databases.GetByName($Database)
...with the difference being that the former returns $null if no such database exists and the latter throws an exception.
If all else fails you could retrieve the desired database like this...
$as.Databases | Where-Object { $_.ID -eq $Database }
...or like this...
$as.Databases | Where-Object { $_.Name -eq $Database }
...depending on which property corresponds to the value in $Database.
Finally, in your code you are attempting to access a DefaultCompatibilityLevel property, which I don't see defined in the Database class. There is, however, a CompatibilityLevel property.

How to make a PSCustomObject with Nested Values

I am trying to create a PSCustomObject with nested values and am having a really tough time, I see plenty of examples of hash tables and pscustom objects but for only extremely basic sets of data.
I am trying to create a a pscustom object that will have a server name property and then another property that has an array of services as well as their current running status.
I was able to make a hash table of the services and their running status but when I try to make an object with the has table it doesnt work very well:
hash table contains service names and their running status (stopped or running)
$hashtable
$myObject = [PSCustomObject]#{
Name = "$server"
Services = "$hashtable"
}
I am open to anything really, I have plenty of examples of how to convert items from JSON or XML and would love be able to use those as well but still having the same issue with being able to format the data in the first place.
edit: sorry about some of the vagueness of this post. As some people have already mentioned, the problem was the double qoutes around the hashtable. Everything is working now
As #t1meless noted in the comments, when you enclose a variable in double quotes it will attempt to convert that value to a string. For a Hashtable object, rather than give any information on from the object this will return "System.Collections.Hashtable". If you remove the double quotes, it will store the value of the hashtable as you are intending.
Here is a full example of what pulling the service information from a server and storing the values in a custom object. Note that $server can still be left in quotes as it is a string, but since it's already a string this would be unnecessary.
$myObject = Foreach ($Server in $Servers) {
$hashtable = #{}
Get-Service -ComputerName $Server | ForEach-Object { $hashtable.Add($_.name,$_.Status}
[PSCustomObject]#{
Name = "$Server"
Services = $hashtable
}
}

Get specific objects from Win32_PnpAllocatedResource

So I want to get those Pnp Devices, which use a given memory area (for example starting address = 655360). I'm using CIM/WMI and and the following command will get back resource and PnpEntity associations:
Get-CimInstance -ClassName Win32_PnpAllocatedResource
But after that how can I get the Win32_PnpEntity associated to Win32_DeviceMemoryAddress which has the starting address 655360?
So the field that you want is the Antecedent property. You only want the ones that refer to memory allocation, and of those you only want the ones that start at 655360. This is really basic stuff, and if you need to ask how to use a Where statement you probably need to go look on the internet on how to filter an array or something for a more detailed explanation to walk you through things.
Get-CimInstance -ClassName Win32_PnpAllocatedResource | Where{$_.Antecedent -like 'Win32_DeviceMemoryAddress (StartingAddress = 655360)'}
That will only return the entries where the starting address is 655360. You should, in theory, be able to use -eq but for this case it doesn't seem to work as expected so the value may have hidden characters, or may be a value type other than [String] so we have to use -Like or -Match and in this case -Like works fine, and is less complicated.