Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
I've been writing all my output to CSV in my scripts because it's been for human consumption. But I discovered that if one of the properties of your exported objects is an array, export-csv doesn't like that (just prints System.Object[] instead the values in the array). So now I've discovered outputting XML and JSON. This changes everything!

Adbot
ADBOT LOVES YOU

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Ithaqua posted:

Agreed on most points. I don't agree with writing a WebAPI service to provide a RESTful API for a simple PS script, though -- that turns into a whole deployment and maintenance scenario. Any well-architected, modern application should be able to perform logging and handle error conditions itself without needing an external helper script. Since we're talking about an external helper script, we can assume it's probably a legacy application that's already suffering from spaghetti dependency syndrome. Adding WebAPI on top of that is just throwing another noodle in the pile.

For a modern application? Especially one that's public-facing? Absolutely, expose a REST API and write a module for it!

Yeah, I'm certainly not saying this is something to do for every situation. It's a large undertaking even when it's a simple database and all you need is read-only access; not something to be taken lightly.

In my case it's not so much about managing the application, or the database itself, it's often tying some administrative/operations process into whatever that existing app/DB does/stores. Like being able to use that information in other processes (which are done exclusively via script), without making the scripts depend directly on the database (its server name, its schema, etc.) and without embedding a crapload of SQL statements.

FISHMANPET posted:

I've been writing all my output to CSV in my scripts because it's been for human consumption. But I discovered that if one of the properties of your exported objects is an array, export-csv doesn't like that (just prints System.Object[] instead the values in the array). So now I've discovered outputting XML and JSON. This changes everything!
Export-Clixml and Import-Clixml are really useful for complex objects, especially for a [PSCredential] object, so you can easily use encrypted passwords.

Swink
Apr 18, 2006
Left Side <--- Many Whelps
Amazing discovery: run "invoke-item ." To open the current directory in Explorer.

But use the alias:

code:
ii  .

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Swink posted:

Amazing discovery: run "invoke-item ." To open the current directory in Explorer.

But use the alias:

code:
ii  .
I usually use start . since I'm used it from the pre-powershell command line. It still works in powershell even though ii is probably a better analogue to what start does in cmd.exe.

Hadlock
Nov 9, 2004

Well originally I was using osql where I could just pass in a small simple query, but as soon as they figured out I knew what the hell I was doing they kept piling more and more on me, and I went from simple sub-10 line queries to finding out that there's an exactly 1008 character limit to osql queries on the command line.

I have one script that queries a configuration database with a couple joins that determines what and in what order to remove servers from our load balancer during code deployment. We bought one software suite I recommended which has deep hooks for powershell and I think the plan was to purchase another suite about this time this year.

I think the plan was that I would eventually become supervisor of our code deployment team, but then our manager died and post-merger they decided to spin our group off to the software vendor so welp.

So now I'm furiously writing scripts to keep everything afloat until the end of the year when either I get my offer letter from the vendor or I get formally reassigned under our app dev group.

We're way past the point where we should have gotten some sort of management framework, but I still feel like I should be able to say something like

code:
$credentials = cred -user "user" -pass "pass"
$connection = sqlprodall,2020
$query = "select * from table where info_date like '%2015%'"

$sqlobject = get-query $connection $credentials $query

$row1column2 = get-sqlobject $sqlobject -row 1 -column 2

#or

$sqlobject.column[2].row[1]

$column3 = get-sqlobject $sqlobject -row * column 3

#or

$sqlobject.column[3]


Hadlock fucked around with this message at 04:48 on Oct 16, 2015

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Hadlock posted:

Well originally I was using osql where I could just pass in a small simple query, but as soon as they figured out I knew what the hell I was doing they kept piling more and more on me, and I went from simple sub-10 line queries to finding out that there's an exactly 1008 character limit to osql queries on the command line.

I have one script that queries a configuration database with a couple joins that determines what and in what order to remove servers from our load balancer during code deployment. We bought one software suite I recommended which has deep hooks for powershell and I think the plan was to purchase another suite about this time this year.

I think the plan was that I would eventually become supervisor of our code deployment team, but then our manager died and post-merger they decided to spin our group off to the software vendor so welp.

So now I'm furiously writing scripts to keep everything afloat until the end of the year when either I get my offer letter from the vendor or I get formally reassigned under our app dev group.

We're way past the point where we should have gotten some sort of management framework, but I still feel like I should be able to say something like

code:
$credentials = cred -user "user" -pass "pass"
$connection = sqlprodall,2020
$query = "select * from table where info_date like '%2015%'"

$sqlobject = get-query $connection $credentials $query

$row1column2 = get-sqlobject $sqlobject -row 1 -column 2

#or

$sqlobject.column[2].row[1]

$column3 = get-sqlobject $sqlobject -row * column 3

#or

$sqlobject.column[3]



Not sure if you'll find anything you need here, but Chrissy LeMaire has some excellent SQL PowerShell scripts on her blog.

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
I've reached the limit of where the SCCM Cmdlets can take me, so I'm having to branch out into WMI queries for some of my stuff. And I have to say I'm just sort of lost. If I'm lucky enough to find someone online doing the same thing as I am I can just copy their query. But once I try and modify them I get lost, and I'm not really sure where to look for more help. I can find the MSDN documentation on the WMI classes, but that's pretty useless. Maybe I need a language primer/tutorial? Not really sure. I know some basic SQL so it's not like a WQL query is completely foreign to me.

Here's the specific problem I'm working with. First, consider this:
code:
$ContentData = Get-WmiObject -Namespace "root\SMS\site_$($SiteCode)" -Query "SELECT SMS_PackageToContent.ContentID from SMS_PackageToContent JOIN SMS_CIToContent on SMS_CIToContent.ContentID = SMS_PackageToContent.ContentID where SMS_CIToContent.CI_ID in ($($CIID))" -ComputerName $SiteServer -ErrorAction Stop
So that works. I honestly don't understand the join that's happening, but it successfully returns the things that have the CI_ID $CIID.

Ok I figured out the join and why I don't need it, and rewrote the query like this:
code:
$ContentData = get-wmiobject -namespace "root\SMS\site_$($SiteCode)" -query "Select * from SMS_CIToContent where CI_ID = $CI_ID" -ComputerName $SiteServer
So I'm trying to speed my script up, because it has 1000 of those (actually exactly 1000, oddly enough). So instead of running through a for loop where I run that query 1000 times, I'm trying to pass in an array of CI_IDs. But I don't know if that's even possible, and if it is possible, how I would go about modifying the "WHERE" clause.

To add more confusion to this, when I try and find articles about the WHERE clause, "in" isn't one of the listed operators, and I in fact found a Stackoverflow question that is my exact question, and the answer was "WQL doesn't have an IN operator" and yet there it is and it works. So I can't even really do research on what the found code is doing, because apparently what it's doing is impossible!
http://stackoverflow.com/questions/19530825/in-operator-in-wql

FISHMANPET fucked around with this message at 23:53 on Oct 16, 2015

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

FISHMANPET posted:

Ok I figured out the join and why I don't need it, and rewrote the query like this:
code:
$ContentData = get-wmiobject -namespace "root\SMS\site_$($SiteCode)" -query "Select * from SMS_CIToContent where CI_ID = $CI_ID" -ComputerName $SiteServer
So I'm trying to speed my script up, because it has 1000 of those (actually exactly 1000, oddly enough). So instead of running through a for loop where I run that query 1000 times, I'm trying to pass in an array of CI_IDs. But I don't know if that's even possible, and if it is possible, how I would go about modifying the "WHERE" clause.
The only way I can think of to speed up 1,000 WMI calls is to parallelize. You could convert this into a workflow and use foreach -parallel or you could use job (probably not a good idea here), or runspaces.

Venusy
Feb 21, 2007
You may be able to speed it up a little bit by creating a CIM session to $SiteServer:
code:
$option = New-CIMSessionOption -Protocol DCOM #assuming WSMan isn't an option
$session = New-CIMSession -ComputerName $SiteServer -SessionOption $option

foreach ($x in $y) {
    $ContentData = Get-CimInstance -namespace "root\SMS\site_$($SiteCode)" -query "Select * from SMS_CIToContent where CI_ID = $CI_ID" -CimSession $session
    ...
}

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Venusy posted:

You may be able to speed it up a little bit by creating a CIM session to $SiteServer:
code:
$option = New-CIMSessionOption -Protocol DCOM #assuming WSMan isn't an option
$session = New-CIMSession -ComputerName $SiteServer -SessionOption $option

foreach ($x in $y) {
    $ContentData = Get-CimInstance -namespace "root\SMS\site_$($SiteCode)" -query "Select * from SMS_CIToContent where CI_ID = $CI_ID" -CimSession $session
    ...
}

D'Oh! That's a much better first step. I looked for the -ComputerName parameter first so I could suggest that, and I completely missed it.

If using the CIM cmdlets doesn't quite work right for whatever reason, and you have powershell remoting enabled on the machine, you can also do the WMI calls through remoting (don't supply -ComputerName in that instance).

Hadlock
Nov 9, 2004

I did something interesting today. I don't want to try and reproduce it on my home computer today/now but this was able to consistently crash clr.dll in Powershell ISE on a patched WS2008 R2 box running $psversiontable.version 4

psuedo-code

php:
<?
$global:array += @()
$global:array += "stuff"

function checkpath{

$path = "c:\fldr\"
$directory = get-childitem $path
  if($global:array.contains($directory.FullName)){} #do nothing
  else{
    #do stuff
  }

}

checkpath
?>
Specifically when I closed the { with a } on the if line, I guess the ISE intellisense would try and evaluate that, it would just loving crash. If I closed the { and hit ctrl+s that would prevent the crash. If I commented out the line, closed the { and then uncommented the line, it would crash. This happened about three times. Just blew my mind.

Any idea here? I've never ever seen PowershellISE.exe loving crash, let alone due to clr.dll. Holy poo poo :psyduck:

edit: tried it at home, did not work. I will give it another go with the actual code. It's reproducible on that one machine though...

double edit: clarified intellisense, added runnable code

Hadlock fucked around with this message at 01:40 on Oct 20, 2015

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Hadlock posted:

I did something interesting today. I don't want to try and reproduce it on my home computer today/now but this was able to consistently crash clr.dll in Powershell ISE on a patched WS2008 R2 box running $psversiontable.version 4

psuedo-code

php:
<?
$global:array += @()
$global:array += "stuff"

function checkpath{

$path = "c:\fldr\"
$directory = get-childitem $path
  if($global:array.contains($directory.FullName)){} #do nothing
  else{
    #do stuff
  }

}

checkpath
?>
Specifically when I closed the { with a } on the if line, I guess the ISE intellisense would try and evaluate that, it would just loving crash. If I closed the { and hit ctrl+s that would prevent the crash. If I commented out the line, closed the { and then uncommented the line, it would crash. This happened about three times. Just blew my mind.

Any idea here? I've never ever seen PowershellISE.exe loving crash, let alone due to clr.dll. Holy poo poo :psyduck:

edit: tried it at home, did not work. I will give it another go with the actual code. It's reproducible on that one machine though...

double edit: clarified intellisense, added runnable code

I've seen ISE crash based on using windows forms, but nothing like you've described with intellisense. There's always the possibility that if it only happens on the one machine, it's some kind of installation problem, dll mismatch, corrupted cache, I dunno. Also could be bad RAM (I hate saying this because it's such a boogeyman). So weird.

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
I'm back with another optimization question! (I gave up on that last one and just let the loop run 1000 queries because it was basically a one off thing)

So now I'm working on a script to check some stuff in SCCM and notify people if they're doing dumb stuff, so this will be running at regular intervals. Part of it is that I have an object array of 1600 Collection objects. If you know anything about SCCM, we have a complex setup of limiting collections, and I'm trying to find the "root" limiting collection for each collection. So I have this poor man's recursion:

code:
function Get-CMUnit($collection) {
    $loopCollection = $collection
    do {
        $limit = $loopCollection.LimitToCollectionName
        if ($limit -eq "All Systems") {
            $properties = @{
                Collection = $collection.Name
                Scope = $loopCollection.Name
            }
            $scope = New-Object pscustomobject -Property $properties
        } else {
            $loopCollection = $collections | where-object {$_.Name -eq $loopCollection.LimitToCollectionName}
        }
    } until ($scope)
    $scope
I then call this in a foreach loop for all 1600 Collections. Basically, for each collection, if it's "limiting collection" is "All Systems" (the base) it returns the name of itself. Otherwise, it looks through the 1600 length object array for the limiting collection, so it can see if that collection is limited to All Systems. This script takes about an hour to run on my laptop, and based on a counter I just put in and having it run for a few minutes, it searches the 1600 item object array 3000-5000 times. So I'm looking for a way to speed that up. There's a whole bunch of properties in each collection object, most of which I don't need. Would it be faster if I used select-object to filter to only the properties I care about? Is there some other way I could be doing this to speed it up?

It'll run at night so ultimately I don't care how long it takes but it's a bear to test and if I can optimize it that'd be good either way, and also learning new things is good too.

Roargasm
Oct 21, 2010

Hate to sound sleazy
But tease me
I don't want it if it's that easy
e: Figured this out. Going to use DirectoryName.substring(3) to get the file path without a drive letter so the end result looks like this:
PHP code:
$mytxt = ls c:\users -recurse -filter *.txt
$copypath = 'd:\copies'

foreach ($txt in $mytxt)
    {
    $txtpath = $txt.DirectoryName.substring(3)
    $destination = $copypath + $txtpath
    if (!(Test-Path -Path $destination)) {New-Item $destination -Type Directory}
    Copy-Item $txt.FullName -Destination $destination -Force
    }
Basic loop to recurse through a big directory, find .encrypted files that were last modified before date $delDate, and move them to another folder.

How do I preserve the folder structure at the destination? Everything is in "D:\ABCPath\A\Myfile.encrypted", "D:\ABCPath\B\Myfile.encrypted", "D:\ABCPath\C\MyFile.encrypted"

I'd like to keep these files in the directories $phase2tempDir\A, $phase2tempDir\B, $phase2tempDir\C, but only copy the folder over if that folder contains an encrypted file that was last modified before $delDate. Right now it's mashing them all together and replacing existing .encrypted files with the same name in the root directory.

PHP code:
echo "***Phase 2- Moving encrypted files to temp folder:", (Get-Date).DateTime >> $logFileName
mkdir $phase2TempDir

$encryptedFilesToMove = Get-ChildItem -Recurse $ABCPath -Filter "*.encrypted" | Where-Object {$_.LastWriteTime -le $delDate}
foreach ($encryptedFile in $encryptedFilesToMove)
    {
    echo "Moving file: $encryptedFile from $ABCPath to $phase2TempDir" >> $logFileName
    Move-Item $encryptedFile.FullName -Destination $phase2TempDir -Force
    }

Roargasm fucked around with this message at 01:21 on Oct 27, 2015

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
So I know that this line specifically is what's taking so long, because it's where I'm searching through 1600 items:
code:
$loopCollection = $collections | where-object {$_.Name -eq $loopCollection.LimitToCollectionName}
I changed the format of the where-object command and cut the speed by 60%. Using the function method finding the scope on the first 100 items took 5.08 minutes.

With this code:
code:
$loopCollection = $collections | where-object -Property Name -EQ -Value $loopCollection.LimitToCollectionName
On the first 100 items that took 2.10 minutes. So that's pretty good.

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
And Victory!

I limited the properties in $collections to only those I wanted. So basically:
code:
$collections = $collections | select-object Name,LimitedToCollectionName,RefreshSchedule
With that little change, it took 4.5 seconds to operate on the first 100 items, and 1.21 minutes to operate on the entire set of 1600 items.

So the moral of the story is, the bigger your object, the longer where-object spends looking through it!

Hadlock
Nov 9, 2004

I tried doing this today

C# code:

params(
  $serverslist
  )

try{
  icm $serverslist { $env:COMPUTERNAME; &iisreset }
  }
catch{
  Write-Host "whoa! bad things happened!
  Exit 1
  }
now, if $serverslist = IISSERVER1 , then it's fine

And if you go in to powershell ISE and do this

C# code:

$serverslist = "IISSERVER1,IISSERVER2,IISSERVER3"

try{
  icm $serverslist { $env:COMPUTERNAME; &iisreset }
  }
catch{
  Write-Host "whoa! bad things happened!
  Exit 1
  }
It works, but if you do this

cmd.exe /c "powershell.exe -file \\path\to\code.ps1 -serverslist 'IISSERVER1,IISSERVER2,IISSERVER3'"

It just blows up

I'm guessing there's special handling of arguments when passing in parameters with commas in them, and they're not treated literally.

What if I did this?

cmd.exe /c "powershell.exe -file \\path\to\code.ps1 -serverslist 'IISSERVER1`,IISSERVER2`,IISSERVER3'"

With backticks after the , to have it interpret it literally?

Toshimo
Aug 23, 2012

He's outta line...

But he's right!
nvm

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Hadlock posted:

I tried doing this today

C# code:

params(
  $serverslist
  )

try{
  icm $serverslist { $env:COMPUTERNAME; &iisreset }
  }
catch{
  Write-Host "whoa! bad things happened!
  Exit 1
  }
now, if $serverslist = IISSERVER1 , then it's fine

And if you go in to powershell ISE and do this

C# code:

$serverslist = "IISSERVER1,IISSERVER2,IISSERVER3"

try{
  icm $serverslist { $env:COMPUTERNAME; &iisreset }
  }
catch{
  Write-Host "whoa! bad things happened!
  Exit 1
  }
It works, but if you do this

cmd.exe /c "powershell.exe -file \\path\to\code.ps1 -serverslist 'IISSERVER1,IISSERVER2,IISSERVER3'"

It just blows up

I'm guessing there's special handling of arguments when passing in parameters with commas in them, and they're not treated literally.

What if I did this?

cmd.exe /c "powershell.exe -file \\path\to\code.ps1 -serverslist 'IISSERVER1`,IISSERVER2`,IISSERVER3'"

With backticks after the , to have it interpret it literally?

What if you remove the single quotes? Don't escape the commas.

Hadlock
Nov 9, 2004

If I did a "Write-Host $servernames" it output server1,server2,server3

The main problem was putting $servernames in the body of a send-mailmessage

code:
10/27/2015 1:06:42 PM All Tasks complete Exit [0]
A positional parameter cannot be found that accepts argument 'SUCCESS'.
    + CategoryInfo          : InvalidArgument: (:) [Send-MailMessage], Paramet 
   erBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell 
   .Commands.SendMailMessage
    + PSComputerName        : server99
This is the send-mailmessage I'm using. I use it all over the place and it works great, but this is the first time I'm using comma seperated values in a variable inside the html body

ICM $emailhost {param($servernames) send-mailmessage -to 'Leroy Jeknins <recipient@company.com>' -from 'name <name@fizzbuzz.com>' -subject "[ SUCCESS ] AUTH IISRESET Report in $env:USERDOMAIN - $(Get-Date -format g)" -body "All server Resets completed, ran on servertype:<br> $servernames ; <br><Br> Script completed at $(Get-Date -format T) ." -BodyAsHtml -smtpServer pop.company.com} -ArgumentList $servernames

I thought for a while maybe it was having an issue with the [ BRACKETS ] so I took that out of the equasion, even tried playing around with the double and single quotes.

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Hadlock posted:

If I did a "Write-Host $servernames" it output server1,server2,server3

The main problem was putting $servernames in the body of a send-mailmessage

code:
10/27/2015 1:06:42 PM All Tasks complete Exit [0]
A positional parameter cannot be found that accepts argument 'SUCCESS'.
    + CategoryInfo          : InvalidArgument: (:) [Send-MailMessage], Paramet 
   erBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell 
   .Commands.SendMailMessage
    + PSComputerName        : server99
This is the send-mailmessage I'm using. I use it all over the place and it works great, but this is the first time I'm using comma seperated values in a variable inside the html body

ICM $emailhost {param($servernames) send-mailmessage -to 'Leroy Jeknins <recipient@company.com>' -from 'name <name@fizzbuzz.com>' -subject "[ SUCCESS ] AUTH IISRESET Report in $env:USERDOMAIN - $(Get-Date -format g)" -body "All server Resets completed, ran on servertype:<br> $servernames ; <br><Br> Script completed at $(Get-Date -format T) ." -BodyAsHtml -smtpServer pop.company.com} -ArgumentList $servernames

I thought for a while maybe it was having an issue with the [ BRACKETS ] so I took that out of the equasion, even tried playing around with the double and single quotes.

If your Write-Host is showing commas, then that's incorrect. That's why I was saying to remove the single quotes, as that would pass your computer list a single string with all the names separated by commas. What you want is an array of strings. Write-Host should print an array as all of the elements separated by spaces or newlines.

Roargasm
Oct 21, 2010

Hate to sound sleazy
But tease me
I don't want it if it's that easy
Mailmessage body hates anything besides HTML and strings. I don't get what the error message is about, but try converting your serverlist into a string and hold it in a separate variable for the email

Harl37
Oct 13, 2005

So very tired.
I'm trying to put a script together in Powershell to reformat a file, and having some troubles.

code:
foreach ($line in $file)
{
	if ($line.StartsWith("2") -eq $true)
		{
			do stuff
		}
}
It's the StartsWith() part, if I specify a number, the script works like I want it to. But what I want the StartsWith() to match is any number 0-9. Any variation of [0-9] I put in there and the "do stuff" part doesn't happen. Is there an easy way to make that happen?

Toshimo
Aug 23, 2012

He's outta line...

But he's right!

Harl37 posted:

I'm trying to put a script together in Powershell to reformat a file, and having some troubles.

code:
foreach ($line in $file)
{
	if ($line.StartsWith("2") -eq $true)
		{
			do stuff
		}
}
It's the StartsWith() part, if I specify a number, the script works like I want it to. But what I want the StartsWith() to match is any number 0-9. Any variation of [0-9] I put in there and the "do stuff" part doesn't happen. Is there an easy way to make that happen?

StartsWith isn't a Powershell method, it's a .Net String method and it only takes strings, not regexes.

Just use:
if ($line -match "^2")

orange sky
May 7, 2007

Harl37 posted:

I'm trying to put a script together in Powershell to reformat a file, and having some troubles.

code:
foreach ($line in $file)
{
	if ($line.StartsWith("2") -eq $true)
		{
			do stuff
		}
}
It's the StartsWith() part, if I specify a number, the script works like I want it to. But what I want the StartsWith() to match is any number 0-9. Any variation of [0-9] I put in there and the "do stuff" part doesn't happen. Is there an easy way to make that happen?

Maybe use something like $a = 0..9 if($a -contains $line.SubString(0,0)) ?

orange sky
May 7, 2007

BTW, very useful tip from powershell.com today:

code:
Today's tip is using the programmable CommandNotFoundHandler to have PowerShell talk with you once you enter an unknown command:
 
$ExecutionContext.InvokeCommand.CommandNotFoundAction =
{
  param(
    [string]
    $commandName,
 
    [System.Management.Automation.CommandLookupEventArgs]
    $eventArgs
  )
 
  $Sapi = New-Object -ComObject Sapi.SpVoice
  $null = $Sapi.Speak("I don't know $commandName, stupid.")
} 
:D have fun!

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Harl37 posted:

I'm trying to put a script together in Powershell to reformat a file, and having some troubles.

code:
foreach ($line in $file)
{
	if ($line.StartsWith("2") -eq $true)
		{
			do stuff
		}
}
It's the StartsWith() part, if I specify a number, the script works like I want it to. But what I want the StartsWith() to match is any number 0-9. Any variation of [0-9] I put in there and the "do stuff" part doesn't happen. Is there an easy way to make that happen?

Use regex with the -match operator:

code:
if ($line -match '^[0-9]') {
    #do stuff
}
Note that \d also represents [0-9] in regex:

code:
if ($line -match '^\d') {
    #do stuff
}
The caret ^ means to match at the beginning of the line.

Adding -eq $true is often redundant. When the expression you're testing evaluates to a [bool] you can just leave it off.

22 Eargesplitten
Oct 10, 2010



I'm getting an access denied error while trying to move items. Which is weird, because I could move one item successfully. It's only when I'm trying to move multiple that it doesn't work.

code:
$downloads = "C:\Users\me\downloads\test1"
$OldItems = gci $downloads | Where-Object {$_.LastAccessTime -lt (get-date).AddDays(-30)}
ForEach-Object { move-item -LiteralPath C:\users\me\Downloads -destination "C:\Users\me\Downloads\Test2"} -InputObject $OldItems
The error is

code:
Move-Item : Access to the path 'C:\users\me\Downloads' is denied.
At line:3 char:27
+ ForEach-Object { move-item <<<<  -LiteralPath C:\users\me\Downloads -destination "C:\Users\me
 -InputObject $OldItems
    + CategoryInfo          : WriteError: (C:\users\me\Downloads:DirectoryInfo) [Move-Item], IOExc
    + FullyQualifiedErrorId : MoveDirectoryItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand
I'm running the ISE as administrator. Does anyone have any idea? I know it's probably ugly as hell, this is me learning by doing. I'm also reading A Month of Lunches, this is just something I've wanted to do for a while. Eventually it's going to move all of the old files from Downloads to Downloads Archive, and do the same thing with Documents and Documents archive.

22 Eargesplitten fucked around with this message at 04:08 on Nov 2, 2015

Toshimo
Aug 23, 2012

He's outta line...

But he's right!

22 Eargesplitten posted:

I'm getting an access denied error while trying to move items. Which is weird, because I could move one item successfully. It's only when I'm trying to move multiple that it doesn't work.

code:
$downloads = "C:\Users\me\downloads\test1"
$OldItems = gci $downloads | Where-Object {$_.LastAccessTime -lt (get-date).AddDays(-30)}
ForEach-Object { move-item -LiteralPath C:\users\me\Downloads -destination "C:\Users\me\Downloads\Test2"} -InputObject $OldItems
The error is

code:
Move-Item : Access to the path 'C:\users\me\Downloads' is denied.
At line:3 char:27
+ ForEach-Object { move-item <<<<  -LiteralPath C:\users\me\Downloads -destination "C:\Users\me
 -InputObject $OldItems
    + CategoryInfo          : WriteError: (C:\users\me\Downloads:DirectoryInfo) [Move-Item], IOExc
    + FullyQualifiedErrorId : MoveDirectoryItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand
I'm running the ISE as administrator. Does anyone have any idea? I know it's probably ugly as hell, this is me learning by doing. I'm also reading A Month of Lunches, this is just something I've wanted to do for a while. Eventually it's going to move all of the old files from Downloads to Downloads Archive, and do the same thing with Documents and Documents archive.

Have you checked to make sure you are actually getting any $OldItems and that just touching the files hasn't reset all their LastAccessTime to Now?

22 Eargesplitten
Oct 10, 2010



Yes, I am. Moving the item isn't changing the access time. I ended up getting it working, but only if I'm doing \downloads\test1, which is what I meant it to be. Unfortunately, if I get it past testing, that's what it needs to be.

Toshimo
Aug 23, 2012

He's outta line...

But he's right!

22 Eargesplitten posted:

Yes, I am. Moving the item isn't changing the access time. I ended up getting it working, but only if I'm doing \downloads\test1, which is what I meant it to be. Unfortunately, if I get it past testing, that's what it needs to be.

This does the job for me (assuming "test1" and "Test2" are directory names):
code:
$UserName  = "foo"
$Downloads = "C:\Users\$UserName\Downloads\test1"
$OldItems = Get-ChildItem $Downloads | Where-Object {$_.LastAccessTime -lt (Get-Date).AddDays(-30)} | Select Name
$OldItems | % { Move-Item -LiteralPath "C:\users\$UserName\Downloads\$($_.Name)" -Destination "C:\Users\$UserName\Downloads\Test2" -WhatIf}
Obviously, remove the WhatIf when you are ready for prime time.

22 Eargesplitten
Oct 10, 2010



Thanks. I can't believe I forgot the Pipe, that was one of the parts that I've read in A Month Of Lunches.

From what I was doing, it looks like I was making several mistakes. First, I was doing \downloads, rather than selecting objects from downloads. Then I was moving all of the items in the folder, regardless of age. That's because to fix the first problem, I did \downloads\* instead of the $($_.Name). For some reason, I thought that ForEach would be only going through the objects in $OldItems.

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy
Hey guys, I've been attending the Mississippi PowerShell User Group meetings (I started 2 months ago). It's all virtual, so you can attend from any location. The next meeting is tonight: http://mspsug.com/2015/11/10/mississippi-powershell-user-group-november-virtual-meeting-tonight-at-830pm-cst/

The Lync/Skype for Business client is a bit weird, so you might want to test it out before hand.

Roargasm
Oct 21, 2010

Hate to sound sleazy
But tease me
I don't want it if it's that easy

Briantist posted:

Hey guys, I've been attending the Mississippi PowerShell User Group meetings (I started 2 months ago). It's all virtual, so you can attend from any location. The next meeting is tonight: http://mspsug.com/2015/11/10/mississippi-powershell-user-group-november-virtual-meeting-tonight-at-830pm-cst/

The Lync/Skype for Business client is a bit weird, so you might want to test it out before hand.

What's the talent level look like in there? Looks cool but do they cover higher level stuff?

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Roargasm posted:

What's the talent level look like in there? Looks cool but do they cover higher level stuff?
So far I've only attended 2 meetings. Typically there is a speaker/presenter (MVPs doing presentations on whatever). The first one I saw was Boe Prox presenting on his PoshRsJob module (parallelization with runspaces), the second was a beginner introduction to regular expressions in powershell by Timothy Warner.

For the most part, there's a bit of Q&A at the end of the presentations and then everyone leaves.

This time there's no presenter so I'm hoping for a more lively / involved discussion.

Uziel
Jun 28, 2004

Ask me about losing 200lbs, and becoming the Viking God of W&W.
Just dove into powershell today when I got a request that seemed like it would be a pain in the rear end to write in a batch script: take a list of FQDNs and do an nslookup on them.
The only issue I'm encountering is getting the Dns Client info as I'm running on Windows 7 and Get-DnsClient is only available on Windows 8 and above despite Powershell 3.
How else can I grab the client DNS server name and IP address?

Toshimo
Aug 23, 2012

He's outta line...

But he's right!

Uziel posted:

Just dove into powershell today when I got a request that seemed like it would be a pain in the rear end to write in a batch script: take a list of FQDNs and do an nslookup on them.
The only issue I'm encountering is getting the Dns Client info as I'm running on Windows 7 and Get-DnsClient is only available on Windows 8 and above despite Powershell 3.
How else can I grab the client DNS server name and IP address?

Does "Get-WMIObject -Class Win32_NetworkAdapterConfiguration" not contain what you are looking for?

12 rats tied together
Sep 7, 2006

Yeah, gwmi win32_networkadapterconfiguration has a property called DnsServerSearchOrder or something that should contain an array of your configured dns servers.

This seems like something that could be done better elsewhere, though, or there's more to the task than "run nslookup on some fqdns". There are a lot of modules for getting various things from your DHCP and DNS servers and barring some insane weirdness in your environment, you should be able to get just about everything you need from there. It's been a long time since I've messed around with windows domains but don't you normally push DNS server configurations as part of a DHCP lease? It would probably be a lot easier to get them from there in that case.

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Reiz posted:

Yeah, gwmi win32_networkadapterconfiguration has a property called DnsServerSearchOrder or something that should contain an array of your configured dns servers.

This seems like something that could be done better elsewhere, though, or there's more to the task than "run nslookup on some fqdns". There are a lot of modules for getting various things from your DHCP and DNS servers and barring some insane weirdness in your environment, you should be able to get just about everything you need from there. It's been a long time since I've messed around with windows domains but don't you normally push DNS server configurations as part of a DHCP lease? It would probably be a lot easier to get them from there in that case.
You can configure an adapter to get its IP address via DHCP but then specify DNS servers manually instead of using the ones from the DHCP server. It's also still common for servers to be configured with static IP and DNS.

So it makes the most sense to retrieve whatever the adapter is actually using, and given the OS restrictions here WMI might be the most straightforward way.

I'm not clear why these servers are even needed to do a name lookup though.

Uziel posted:

Just dove into powershell today when I got a request that seemed like it would be a pain in the rear end to write in a batch script: take a list of FQDNs and do an nslookup on them.
The only issue I'm encountering is getting the Dns Client info as I'm running on Windows 7 and Get-DnsClient is only available on Windows 8 and above despite Powershell 3.
How else can I grab the client DNS server name and IP address?
You should be able to use the underlying .net object to resolve the names, without knowing the DNS servers:

code:
# I'm assuming $fqdn is an array of names you want to resolve.

$fqdnList | ForEach-Object {
    [System.Net.Dns]::GetHostEntry($_).AddressList
}

Adbot
ADBOT LOVES YOU

Uziel
Jun 28, 2004

Ask me about losing 200lbs, and becoming the Viking God of W&W.

Toshimo posted:

Does "Get-WMIObject -Class Win32_NetworkAdapterConfiguration" not contain what you are looking for?

Reiz posted:

Yeah, gwmi win32_networkadapterconfiguration has a property called DnsServerSearchOrder or something that should contain an array of your configured dns servers.

This seems like something that could be done better elsewhere, though, or there's more to the task than "run nslookup on some fqdns". There are a lot of modules for getting various things from your DHCP and DNS servers and barring some insane weirdness in your environment, you should be able to get just about everything you need from there. It's been a long time since I've messed around with windows domains but don't you normally push DNS server configurations as part of a DHCP lease? It would probably be a lot easier to get them from there in that case.
I am going to upgrade to PS4 tomorrow and see. I wasn't aware of gwmi win32_networkadapterconfiguration as this is the first time I've used powershell. I'm a developer so don't know much about the IT configuration stuff. The request was literally "here are a list of FQDNs and I want the output in this exact format".

The real input are all internal servers, but for example:
input:
https://www.google.com
output:
Server: ns1.ispname.com, Address: 192.168.1.1, https://www.google.com 2607:f8b0:4004:809::1003, 216.58.217.142



Edit: In looking at this, I need to know which DNS server (name/ip) is being used for each nslookup to get those IP Addresses. Is this possible?

Uziel fucked around with this message at 14:21 on Nov 13, 2015

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply