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
Raere
Dec 13, 2007

Oh, I'm dumb. Scriptblocks don't need to be in quotes. Jobs are workable for me now. I had the entire function in quotes, using backticks at the end of every line.

Raere fucked around with this message at 00:59 on Sep 19, 2015

Adbot
ADBOT LOVES YOU

Venusy
Feb 21, 2007
Running into a weird issue - PowerShell remoting isn't enabled in our environment, so I have to use WMI jobs to query computers in parallel. ($job = Get-WMIObject -Class [not important for this example] -ComputerName $computername-AsJob)

This works fine if I run the command interactively as myself or as the account I want to use to eventually run the same queries. I have a scheduled job using this command that runs on my workstation under my account while I am logged in (created using Register-ScheduledJob), and that works fine as well.

However, creating the task in Task Scheduler on one of our servers, with the account set to the account I want to run the task (and that it should run whether the user is logged on or not) fails. Other Task Scheduler jobs that do not use WMI jobs complete successfully. Going by the $Error variable, the fault is apparently an invalid parameter somewhere in this line and a similar one that calls Receive-Job instead of Wait-Job:
foreach ($computer in $computername) {
$test = $job | Get-Job -IncludeChildJob | Where Location -eq $computer | Wait-Job -Timeout $timeout
...
}

The ideal workaround would be to get PowerShell remoting enabled, but I don't think that's happening here. But I'm kind of stumped about what parameter it's actually seeing as invalid, or of any workaround other than removing the job and just letting WMI query in serial - which would likely work, it'd just take forever.

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Venusy posted:

Running into a weird issue - PowerShell remoting isn't enabled in our environment, so I have to use WMI jobs to query computers in parallel. ($job = Get-WMIObject -Class [not important for this example] -ComputerName $computername-AsJob)

This works fine if I run the command interactively as myself or as the account I want to use to eventually run the same queries. I have a scheduled job using this command that runs on my workstation under my account while I am logged in (created using Register-ScheduledJob), and that works fine as well.

However, creating the task in Task Scheduler on one of our servers, with the account set to the account I want to run the task (and that it should run whether the user is logged on or not) fails. Other Task Scheduler jobs that do not use WMI jobs complete successfully. Going by the $Error variable, the fault is apparently an invalid parameter somewhere in this line and a similar one that calls Receive-Job instead of Wait-Job:
foreach ($computer in $computername) {
$test = $job | Get-Job -IncludeChildJob | Where Location -eq $computer | Wait-Job -Timeout $timeout
...
}

The ideal workaround would be to get PowerShell remoting enabled, but I don't think that's happening here. But I'm kind of stumped about what parameter it's actually seeing as invalid, or of any workaround other than removing the job and just letting WMI query in serial - which would likely work, it'd just take forever.

I'm not sure why it's not working, but you could use runspaces instead of jobs for parallelism. There's a module that handles all the runspace stuff: https://github.com/proxb/PoshRSJob

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
tl;dr I'm an idiot don't mind me

OK I just have no idea what's going on, but every time I print anything, "Can't find file " gets put at the beggining.
code:
function Get-FolderSize ($Directory)
{
    $return = (Get-ChildItem -path $Directory.FullName -recurse | Measure-Object -property length -sum).Sum
    $return
}

$folders = gci C:\Temp
foreach($folder1 in $folders)
    {
    #print $folder1
    $name = [GUID]::Empty
    if([Guid]::TryParse($folder1.Name,[ref]$name))
    {
        #print $folder1.Name
        foreach($folder2 in $folders) 
        {
            #print $folder2.Name
            if ((Get-FolderSize $folder1) -eq (Get-FolderSize $folder2))
            {
                #print "$folder1 and $folder2 match!"
                print "match!"
            }
        }
             
    }
}
This should just output the word "match!" a bunch of times because the data I'm running it against has the problem, but instead it outputs "Can't find file match!" instead. If I uncomment any of the other print statements, it tries to put "Can't find file " at the beginning of those too. I don't have any idea what's going on here.

E: AAAAUGH this has infected my entire environment! If I log out and log in, open a powershell window, and just write print "hello" in the window I get back "Can't find file hello"

E2: Why am I using print? Did I fall on my head and think I was writing Python? I guess I'm an idiot and I keep calling the "print" command.

FISHMANPET fucked around with this message at 22:07 on Sep 21, 2015

Toshimo
Aug 23, 2012

He's outta line...

But he's right!
DUDE NO. What did you make me do to my machine?

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
That's kind of impressive, but also shouldn't have worked as written because I was only "printing" the word match.

Toshimo
Aug 23, 2012

He's outta line...

But he's right!

FISHMANPET posted:

That's kind of impressive, but also shouldn't have worked as written because I was only "printing" the word match.

I uncommented the first print.

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
Welp, just printing out every file in your Temp dir, nbd.

Mr Crucial
Oct 28, 2005
What's new pussycat?
What's the best way to set JPG metadata using Powershell? I have a bunch of JPGs with blank titles and subjects. Said titles and subjects are available in a CSV file, I've written a script that retrieves them and attempts to inject them into the JPG properties but it doesn't work:

code:
Set-ItemProperty -Path $fullpath -Name title -Value "$title"
Set-ItemProperty -Path $fullpath -Name Subject -Value "$subject"
Gives me an IO error, because title and subject don't exist as properties. Which sorta makes sense if they are EXIF properties and not file properties.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Mr Crucial posted:

What's the best way to set JPG metadata using Powershell? I have a bunch of JPGs with blank titles and subjects. Said titles and subjects are available in a CSV file, I've written a script that retrieves them and attempts to inject them into the JPG properties but it doesn't work:

code:
Set-ItemProperty -Path $fullpath -Name title -Value "$title"
Set-ItemProperty -Path $fullpath -Name Subject -Value "$subject"
Gives me an IO error, because title and subject don't exist as properties. Which sorta makes sense if they are EXIF properties and not file properties.

You can hook into the COM object that the File Explorer uses to query file metadata.

http://blogs.technet.com/b/heyscriptingguy/archive/2014/02/06/use-powershell-to-find-metadata-from-photograph-files.aspx

https://social.technet.microsoft.com/Forums/scriptcenter/en-US/8b398413-0728-4a8a-a593-d2e8b92b88f2/editing-image-exif-tags?forum=ITCG

Mr Crucial
Oct 28, 2005
What's new pussycat?

Ithaqua posted:

You can hook into the COM object that the File Explorer uses to query file metadata.

Doesn't work, unfortunately - it's okay for reading certain fields but not for writing anything that was useful to me.

In the end I installed ExifTool from here http://www.sno.phy.queensu.ca/~phil/exiftool/ and got Powershell to execute commands against it. Worked perfectly.

skooma512
Feb 8, 2012

You couldn't grok my race car, but you dug the roadside blur.
I'm scratching my head right now with the way this script behaves.

I have a CSV with one column. I'm using Write-Host to see what it's getting read as the output is @{Columnname= List Element}. What am I doing wrong?

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

skooma512 posted:

I'm scratching my head right now with the way this script behaves.

I have a CSV with one column. I'm using Write-Host to see what it's getting read as the output is @{Columnname= List Element}. What am I doing wrong?
Post your code.

Video Nasty
Jun 17, 2003

skooma512 posted:

I'm scratching my head right now with the way this script behaves.

I have a CSV with one column. I'm using Write-Host to see what it's getting read as the output is @{Columnname= List Element}. What am I doing wrong?

using select-object ?

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin
I've got a script based on a dos command. Yeah, I know.

REG QUERY $registrystring

Is there an easy way to redirect the streams to two separate variables, like stderr to $err and stdout to $out

12 rats tied together
Sep 7, 2006

skooma512 posted:

I'm scratching my head right now with the way this script behaves.

I have a CSV with one column. I'm using Write-Host to see what it's getting read as the output is @{Columnname= List Element}. What am I doing wrong?

You have to like, do something with the CSV to be able to write-host it. Unless you actually have a big rear end string (like, if you pasted it and then put quotes around it), in which case you don't actually have a CSV you just have a big rear end string.

If you have a CSV in a file, and you do something like: "$csv_object = Import-Csv .\csv-in-file.txt"...

You now have some amalgamation of a custom object that more or less represents your csv stored inside $csv_object. $csv_object.column1 will show you everything in the first column, where "column1" is the actual name of your column, so $csv_object.date, .time, etc. This output is some form of an array or list or whatever and can be sliced, selected from, and all the other things you learned about in powershell in a month of lunches but didn't have anything to do with and probably forgot about. So, if you want the value of the fourth row in the first column of your csv object you can just do "$csv_object.column1[3]".

But, generically, if you actually have a CSV object it should be really hard to do anything with it that isn't immediately useful or obvious. Like, if you pipe it into get-member it's gonna have like 4 methods and then a bunch of properties that correspond to your stuff.

quote:

Is there an easy way to redirect the streams to two separate variables, like stderr to $err and stdout to $out
You're probably looking for something like Tee-Object. You're going to want some way to determine between stdout and stderr if you don't already have that. If it were me, I would probably want to move away from using "REG QUERY" and just query the registry directly with powershell since there is a provider for it (run get-psprovider) and you can interact with it more or less like it is a filesystem.

12 rats tied together fucked around with this message at 03:19 on Sep 30, 2015

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin

Reiz posted:

You're probably looking for something like Tee-Object. You're going to want some way to determine between stdout and stderr if you don't already have that. If it were me, I would probably want to move away from using "REG QUERY" and just query the registry directly with powershell since there is a provider for it (run get-psprovider) and you can interact with it more or less like it is a filesystem.

Probably. I'm mostly interested because there's a few other legacy systems that I'm in the middle of uplifting to 2008 or 2012 and the good Powershell commands don't always work.

skooma512
Feb 8, 2012

You couldn't grok my race car, but you dug the roadside blur.
I actually figured it out! Though I did it that same way before, it works now.

Briantist posted:

Post your code.


Import-CSV Book1.csv | Foreach-Object {
$computername = $_.computername

Remove-Item -path \\$computername\c$\Test Folder


}

If I simply put $_, it would spit out what I posted originally. Specifying the column name fixed that, though I know I did that before, but it didn't work. That's also the only column in that CSV.



Thanks all!

12 rats tied together
Sep 7, 2006

For what it's worth, if you need to do that in the future it's technically a one liner. To start, a csv with one column is just a list, so you can easily get-content instead of import-csv. But, if for some reason you need to interact with it as a csv (maybe it is generated during some automated process) you can do this:
code:
Import-csv .\whatever | % { remove-item "\\$($_.computername)\c$\folder\thing }
Having a separate line and a bunch of white space to convert a property called computername into..... computername isn't terribly useful. Doing a $() allows you to evaluate whatever is inside the ()s before actually running the command. This could be useful if your csv actually had 2 columns -- one for a computername and another for a specific directory that you needed to delete. Instead of needing to expand out the "script" you could just set the remove item path to "\\$($_.computername)\$($_.directory)\file". Of course, if you're doing anything with file paths in general it is probably better to use join-path.

I imagine both of these methods are interpreted into roughly the same operations but in my opinion there's really no need to spend 8 lines doing something that can be appropriately expressed in 1.

12 rats tied together fucked around with this message at 13:39 on Sep 30, 2015

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin

Reiz posted:

You're probably looking for something like Tee-Object. You're going to want some way to determine between stdout and stderr if you don't already have that. If it were me, I would probably want to move away from using "REG QUERY" and just query the registry directly with powershell since there is a provider for it (run get-psprovider) and you can interact with it more or less like it is a filesystem.

I took a look at this today, I agree with you when it comes to a local computer, it's super easy to navigate the registry as a file system.
I'm running into issues because I need to check the registry on several thousand servers for the presence of a specific key.

I've had mixed results with Invoke-Command, it might be because the target systems are a mix of operating systems. If this is really the way to go then I guess that's ok.

What I've been trying to do is create output like:

SERVERNAME,Present?,Failurereason

The third column is what's giving me a hard time. I want to be able to differentiate between Key not found, Access denied, and network error.

I'll probably be dicking around with this off and on all day but if someone knows an easy way to do it, I'm all ears.

Hadlock
Nov 9, 2004

I would start with a an
code:
foreach($hostname in $hostnames){
$a = 
try{
Invoke-Command $hostname {
  try{
    testpath HKLM:\software\
    #dostuff
    return 0
    }
  catch{
    write-host "registry error on $env:computername"
    return 1
  }
}
catch{
  write-host "network error on $env:computername"
  return 500
  }
if($a -eq 0){
  $goodservers+= $hostname #add to array
  }
else{
  $badservers+= $hostname
  $failurereason+= $a
  $indexincrement++
  }
}	 

Hadlock fucked around with this message at 19:25 on Sep 30, 2015

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
I had to solve a similair problem, mine was changing the password on 1000 servers running everywhere from Server 2003 up to 2012 R2. I was also interested in catching the various failure states.
code:
Param(
    [Parameter(Mandatory=$true)]
    [String[]]
    $ComputerNames,

    [Parameter(Mandatory=$true)]
    [String]
    $LocalAccount
)

$input = Read-Host -Prompt "Enter Password Base" -AsSecureString
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($input))
$input = Read-Host -Prompt "Enter Password Base Again" -AsSecureString
$password1 = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($input))
    if ($password -ne $password1) {
        Write-Error "passwords don't match"
        Exit
    }

$RemoteChangeScript = {
    Param(
        [Parameter(Mandatory=$true, Position=1)]
        [String]
        $password,

        [Parameter(Mandatory=$true, Position=2)]
        [String]
        $LocalAccount,

        #This is here so I can record what the server name that the script connected to was, sometimes the DNS records get messed up, it can be nice to have this.
        [Parameter(Mandatory=$true, Position=3)]
        [String]
        $TargettedServerName
    )
    $LocalUsers = Get-WmiObject Win32_UserAccount -Filter "LocalAccount=true" | Foreach {$_.Name}
    foreach ($User in $LocalUsers)
        {
            if (($LocalAccount -ne $User) -and ($User -ne "Guest"))
            {
                Write-Warning "Server: '$($TargettedServerName)' has a local account '$($User)' whos password is NOT being changed by this script"
            }
        }
    $password = $password + $env:computername[-2].ToString().ToLower() + $env:computername[-1].ToString().ToLower()
    try
    {
        $objUser = [ADSI]"WinNT://localhost/$($LocalAccount), user"
        $objUser.psbase.Invoke("SetPassword", $password)
        $Properties = @{
            TargettedServerName = $TargettedServerName
            Username =  $LocalAccount
            RealServerName = $env:computername
            Error = "Sucess"
        }
    }
    catch
    {
        Write-Warning "Error changing password for user:$($LocalAccount) on server:$($TargettedServerName)"
        $Properties = @{
            TargettedServerName = $TargettedServerName
            Username =  $LocalAccount
            RealServerName = $env:computername
            Error = $_.Exception.Message
        }

    }
    finally
    {
        #Write-Warning $Properties.RealServerName
        $ReturnData = New-Object PSObject -Property $Properties
        Write-Output $ReturnData
    }
}
    
    foreach ($Computer in $ComputerNames)
    {
        #$Return = Invoke-Command -ScriptBlock {echo "helo"} -ComputerName $Computer -AsJob
        try
        {
            $Result = Invoke-Command -ScriptBlock $RemoteChangeScript -ArgumentList @($password, $LocalAccount, $Computer) -ComputerName $Computer -ErrorAction Stop
            #Write-Warning "$computer try"
            #$Result = Receive-Job $Return -ErrorAction Stop
            #Write-Warning $result
            #Write-Warning "hello this is the try block"
            #Write-Warning $result
        }
        catch
        {
            #Write-Warning "$computer catch"
            $Properties = @{
                TargettedServerName = $Computer
                Username =  $LocalAccount
                RealServerName = $Computer
                Error = $_.Exception.Message
            }
            $Result = New-Object PSObject -Property $Properties
        }
        Write-Output $Result
    }

    #$PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword))
There's a lot of ancillary stuff here that was specific to to my task but I'm not going to take it out because I'll probably just turn the code into nonsense. BUT
Basically:
I created $RemoteChangeScript, which is the script I would execute on the remote machine. Then I used a for each loop to go through each computer and try and use invoke-command on that computer. If it failed and went to my catch block, I created a new object that had the computer name (twice, because I got the name 2 ways) and the name of the account I was trying to change, as well as the specific error message, and then returned that. In the RemoteChangeScript if my command to change the password succeeded, I returned a similar object with an "error" message of "success. And if the script failed on the remote computer, I had a catch block that would return an object with that error. So no matter what, my script would return an object with the computer name, the account I was changing, the computer name again, and what the outcome of the command was. This let me separate the successes from the computers I couldn't connect to from the computers that for whatever reason couldn't execute the command locally.

12 rats tied together
Sep 7, 2006

Dr. Arbitrary posted:


SERVERNAME,Present?,Failurereason

The third column is what's giving me a hard time. I want to be able to differentiate between Key not found, Access denied, and network error.

Doing it on thousands of servers is gonna be tricky, yeah. I've done it on ~200 servers just fine with invoke-command though. I imagine an issue you'll run into will be any minor inefficiencies ballooning up into a serious problem.

You might want to look into foreach -parallel. Have a function that takes a computername and runs some tests, start at the first thing that could fail (network) and, on a failure, out-file -append your log message, you can try to script pssessions for the remote reg key check or just cobble together a working invoke.

If you foreach -parallel that function call with a list of computers you should be alright, maybe. You can do it one at a time but with 1000+ computers it's gonna be a while.

Two other things you might wanna check out are the Microsoft.Win32.Registry class (I'm pretty sure this has a method for remote key query) and, if you have to manage thousands of machines, ansible. You will need some kind of Linux machine for ansible, however.

Eggnogium
Jun 1, 2010

Never give an inch! Hnnnghhhhhh!
Powershell remoting question:

I want to run some commands from client A on server B. The two are in different domains so I am passing an IP address and credential parameter to Invoke-Command to force NTLM auth. Everything's gravy as long the account I am using is an administrator on B. But I also want non-admin accounts to be able to run these commands, so I created and registered a new PSSessionConfiguration and gave FullControl to a non-admin account as described here. However, I am still getting access denied when I try to use the new session configuration. The new configuration works if I connect with my admin account.

The specific error is:

Access is denied
+ CategoryInfo : OpenError: (w.x.y.z:String) [], RemoteException
+ FullyQualifiedErrorId : PSSessionStateBroken

Anyone know if there are any other permissions I have to grant to non-admin account? Or how to crack open the error for something a little more specific?

Edit: My command:

code:
Invoke-Command -ComputerName "w.x.y.z" -ConfigurationName Test -Credential $credential -ScriptBlock { Get-Commmand }

Eggnogium fucked around with this message at 23:15 on Oct 1, 2015

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Eggnogium posted:

Powershell remoting question:

I want to run some commands from client A on server B. The two are in different domains so I am passing an IP address and credential parameter to Invoke-Command to force NTLM auth. Everything's gravy as long the account I am using is an administrator on B. But I also want non-admin accounts to be able to run these commands, so I created and registered a new PSSessionConfiguration and gave FullControl to a non-admin account as described here. However, I am still getting access denied when I try to use the new session configuration. The new configuration works if I connect with my admin account.

The specific error is:

Access is denied
+ CategoryInfo : OpenError: (w.x.y.z:String) [], RemoteException
+ FullyQualifiedErrorId : PSSessionStateBroken

Anyone know if there are any other permissions I have to grant to non-admin account? Or how to crack open the error for something a little more specific?

Edit: My command:

code:
Invoke-Command -ComputerName "w.x.y.z" -ConfigurationName Test -Credential $credential -ScriptBlock { Get-Commmand }
I actually just did this today, giving non-admins access to a new PSSessionConfiguration. I didn't have any problem with it, though I wasn't going between domains. I don't see any issue with the client call. When you set the permissions did you use the UI or did you try to write the SDDL yourself?

Eggnogium
Jun 1, 2010

Never give an inch! Hnnnghhhhhh!

Briantist posted:

I actually just did this today, giving non-admins access to a new PSSessionConfiguration. I didn't have any problem with it, though I wasn't going between domains. I don't see any issue with the client call. When you set the permissions did you use the UI or did you try to write the SDDL yourself?

SDDL is gibberish to me so I used -ShowSecurityDescriptorUI. None of the guides I've found said which permissions were required so I granted Full Control.

Interestingly, if I enter the non-admin credentials when trying to connect with the default configuration (for which I haven't changed permissions), I get a slightly different format for the error message:

[w.x.y.z] Connecting to remote server w.x.y.z failed with the following error message : Access is denied.
For more information, see the about_Remote_Troubleshooting Help topic.
+ CategoryInfo : OpenError: (w.x.y.z:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : AccessDenied,PSSessionStateBroken

I guess my next move is to use the PSDiagnostics module to see if I can get a more detailed error message from the WSMan trace... Looks tedious.

Edit: Found the issue via PSDiagnostics logs on the server machine. My session config defined a psm1 to import to the session, and it was sitting on the desktop of my admin account while I was testing. Moved it to a location that any valid user would have access to and it works!

Eggnogium fucked around with this message at 20:10 on Oct 2, 2015

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Eggnogium posted:

SDDL is gibberish to me so I used -ShowSecurityDescriptorUI. None of the guides I've found said which permissions were required so I granted Full Control.

Edit: Found the issue via PSDiagnostics logs on the server machine. My session config defined a psm1 to import to the session, and it was sitting on the desktop of my admin account while I was testing. Moved it to a location that any valid user would have access to and it works!

Yay! Glad you got it working. The minimum permissions you need (if you want to limit it, and you should), are Read and Invoke.

orange sky
May 7, 2007

Okay, this is a huuuge problem, so I need some help in how to tackle it. Only read ahead if you really want to help me :)

We have a list that looks like this in Excel / CSV



Our main objective - get contained groups of users and internetaddresses. We're doing a migration and so we need to migrate shared mailboxes and their users at the same time. That's the objective with this script.

I'm dealing with a problem right now - I want there to be an object like this:

Object
Internetaddress : {a@contoso.com, b@contoso.com, c@contoso.com}
Members: {{User1, User3, User7, User10, User4, User100, User5, User2, User8}, {User10, User8, User5, User2, User20, User3, User4}, {User40, User100, User10}}

Things to note: Users are not repeated in any array and the positions match the association.

Here's what I have right now:

code:

foreach($mailin in $completeList)
{

    $internetaddress = $mailin.internetaddress
    $managers = $mailin.managers -split ","
    $readers = $mailin.readers -split ","
    $senders = $mailin.senders -split ","
    $people = $managers + $readers + $senders

    $people = Sort-Object -Unique -InputObject $people

    $objtemp | add-member -membertype NoteProperty -name "internetaddress" -value $internetaddress
    $objtemp | add-member -MemberType NoteProperty -name "people" -value $people
}

This might be lovely code, but I thought it might do the job.

However, when I use

code:
$objtemp | Where-Object {$_.internetaddress -eq 'a@contoso.com'}
It returns the entire array of {a@contoso.com, b@contoso.com, c@contoso.com}.

What am I missing?

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
I'm confused why you can't just pipe this into import-csv. Is there a reason you need a singular array rather than a list of objects that can be treated like an array?

I suppose that would get you each field as a string, and you'd have to manually split it up into an array and maybe even make a new object but that still seems easier than what you're trying to do.

Wrath of the Bitch King
May 11, 2005

Research confirms that black is a color like silver is a color, and that beyond black is clarity.
Running into an issue with importing printers based on a query. I'm restricted to using GWMI to query rather than the Print Management cmdlets since these are being run on Windows 7 clients.

The general premise is for each client to have a script (either pushed via SCCM or as a part of logon) that will completely remove all connected network printers, query the local print server, then add all network printers it can find.

Removal is the simple part:

code:
gwmi -Class 'Win32_Printer' | where {$_.ShareName -ne $null} | rwmi
This works without any issues and removes only printers that have a defined Share Name.

However, I'm having issues finding something that will allow me to query a print server and pipe the results into an object creation cmdlet. Querying works without issue; the print server and the client workstation have a similar naming convention based on subnet, so we can have a working query using variables extrapolated from the hostname running the client to create the path to the print server that works across multiple locations.

code:
$Subnet = $Env:ComputerName.substring(2,3) #this enumerates the subnet, which is baked into the client hostname
$PrintServer = "20.$subnet.40.20"
gwmi -computername $PrintServer -class 'Win32_Printer' | where {$_.ShareName -ne $null} #print server subnet will always match this pattern
Querying the print server itself for printer objects DOES work and results in a list of POs.

New-Object doesn't support piping, and anything piped to it would need to be made from the [wmi]win32_printer context, so from what I can tell something like this:

code:
gwmi -computername $PrintServer -class 'Win32_Printer' | where {$_.ShareName -ne $null} | foreach {
(New-Object -ComObject WScript.Network).AddWindowsPrinterConnection("\\$PrintServer\$_")
Won't work. Similarly, outputting to text and attempting to inject that way seems to fail. It looks like AddWindowsPrinterConnection() only accepts literal values.

Any help on this would be appreciated, I'm sure I'm missing something obvious but I just can't script anymore today. There seem to be options out there that work with singular printer queues, but none that work enmasse.

Wrath of the Bitch King fucked around with this message at 17:41 on Oct 9, 2015

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Wrath of the Bitch King posted:

code:
gwmi -computername $PrintServer -class 'Win32_Printer' | where {$_.ShareName -ne $null} | foreach {
(New-Object -ComObject WScript.Network).AddWindowsPrinterConnection("\\$PrintServer\$_")
Won't work. Similarly, outputting to text and attempting to inject that way seems to fail. It looks like AddWindowsPrinterConnection() only accepts literal values.

Any help on this would be appreciated, I'm sure I'm missing something obvious but I just can't script anymore today. There seem to be options out there that work with singular printer queues, but none that work enmasse.

Won't work how?

Keep in mind that $_ represents a WMI object here, not a string. The value you get when you inject it into the string will be the same as $_.ToString() , but that's not the same thing you'll see if you just let it get written out to the host, which is the value of $_ | Out-String . These could be the same, but often aren't.

So really, you need to figure out which property of $_ you want, then use "\\$PrintServer\$($_.PropertyName)". It's not possible for AddWIndowsPrinterConnection to know that you're doing variable interpolation inside the string you passed to it (that step happens before it ever enters that method).

Add this inside your loop, write before the New-Object line:

code:
Write-Verbose "\\$PrintServer\$_" -Verbose
That way you can see what you're really passing into it.

Wrath of the Bitch King
May 11, 2005

Research confirms that black is a color like silver is a color, and that beyond black is clarity.
Seriously, thank you loads. I knew it was something stupid, and I had a feeling it had to do with failing to isolate the property.

Hadlock
Nov 9, 2004

So over the last two weeks I've been sort of forced to learn Ruby on Rails (RoR). It's made me re-evaluate how SQL access is implemented in Powershell.

One of the frustrating things about powershell for me has been dealing with SQL, for example I want/need to monitor a table every 15 minutes for a specific case and then log that event and/or take action, or pull data out of our configuration db to handle pulling servers in/out of our load balancer for code deployments etc etc.

Powershell does an awful, awful job of talking to SQL. RoR even takes the actual SQL queries out of the loop entirely. Is there a way to make Powershell + SQL less painful? I guess MS has their LINQ language but I was never able to get that to work and we generally avoid doing anything in powershell with SQL unless it's absolutely necessary (like the load balancer example I gave). Seeing how well RoR talks to the database makes me crazy envious. Was the secret purpose of PS Modules to write the primary language, and hope that the community would come around and build something Rails-ey behind them?

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Hadlock posted:

So over the last two weeks I've been sort of forced to learn Ruby on Rails (RoR). It's made me re-evaluate how SQL access is implemented in Powershell.

One of the frustrating things about powershell for me has been dealing with SQL, for example I want/need to monitor a table every 15 minutes for a specific case and then log that event and/or take action, or pull data out of our configuration db to handle pulling servers in/out of our load balancer for code deployments etc etc.

Powershell does an awful, awful job of talking to SQL. RoR even takes the actual SQL queries out of the loop entirely. Is there a way to make Powershell + SQL less painful? I guess MS has their LINQ language but I was never able to get that to work and we generally avoid doing anything in powershell with SQL unless it's absolutely necessary (like the load balancer example I gave). Seeing how well RoR talks to the database makes me crazy envious. Was the secret purpose of PS Modules to write the primary language, and hope that the community would come around and build something Rails-ey behind them?

import-module sqlps

Also LINQ isn't a language and it's not specifically for SQL. It stands for Language INtegrated Query. There's LINQ to Entities for Entity Framework, but EF is a shitpile anyway. And I've become convinced that SQL-generating ORMs are almost universally more trouble than they're worth.

Powershell is supposed to be more similar to something like Bash than it is to Ruby. Bash doesn't have an ORM, either, because working with databases from a command shell is niche at best.

Hadlock
Nov 9, 2004

Ithaqua posted:

import-module sqlps

Also LINQ isn't a language and it's not specifically for SQL. It stands for Language INtegrated Query. There's LINQ to Entities for Entity Framework, but EF is a shitpile anyway. And I've become convinced that SQL-generating ORMs are almost universally more trouble than they're worth.

Powershell is supposed to be more similar to something like Bash than it is to Ruby. Bash doesn't have an ORM, either, because working with databases from a command shell is niche at best.

Does sqlps even work if you're not directly running on a machine with SQL Server installed? I think I tried going that route and found out you can't install sqlps without SQL Server already being on the machine, which was really annoying since our SQL servers are run by a different group and fenced off from my environment. It does work really excellently for DBA work though.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Hadlock posted:

Does sqlps even work if you're not directly running on a machine with SQL Server installed?

Yes.

http://guidestomicrosoft.com/2015/01/13/install-sql-server-powershell-module-sqlps/

Hadlock
Nov 9, 2004

Oh right, I think we came across that too, but it is not backwards compatible with 2008 non-R2 :( Which is about 90% of my production environment :smithicide:

12 rats tied together
Sep 7, 2006

orange sky posted:

What am I missing?
Knee jerk shooting from the hip solution: change your add member -name properties to be variables instead of static: $internetaddress, etc.

If you can paste your list example in a format I can copy and paste I will play around with it later tonight, assuming the previous doesn't help or get you on the right track.

12 rats tied together fucked around with this message at 19:39 on Oct 12, 2015

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Hadlock posted:

So over the last two weeks I've been sort of forced to learn Ruby on Rails (RoR). It's made me re-evaluate how SQL access is implemented in Powershell.

One of the frustrating things about powershell for me has been dealing with SQL, for example I want/need to monitor a table every 15 minutes for a specific case and then log that event and/or take action, or pull data out of our configuration db to handle pulling servers in/out of our load balancer for code deployments etc etc.

Powershell does an awful, awful job of talking to SQL. RoR even takes the actual SQL queries out of the loop entirely. Is there a way to make Powershell + SQL less painful? I guess MS has their LINQ language but I was never able to get that to work and we generally avoid doing anything in powershell with SQL unless it's absolutely necessary (like the load balancer example I gave). Seeing how well RoR talks to the database makes me crazy envious. Was the secret purpose of PS Modules to write the primary language, and hope that the community would come around and build something Rails-ey behind them?

Well the two languages have entirely different purposes. I mean really, they aren't even both languages. PowerShell is a scripting language/shell environment. RoR is an MVC web platform (rails is the platform, and that's what's abstracting the SQL stuff, Ruby is the language).

Before I learned PowerShell I was using Ruby (not rails) for most of my windows scripting, and using SQL with it is just as painful.

But I get what you're saying; it feels wrong to use SQL directly in scripts, and it kind of is.

My approach to this is usually to separate the concerns. Lately, I've been fond of wrapping SQL databases in a REST API, using an ASP.NET Web API project, with something like Entity Framework as an ORM. This is in C#.

Then a server runs IIS and hosts that app, and in PowerShell I either call the REST API directly (Invoke-RestMethod), or even better, I write a PowerShell module that wraps the API, and then provide familiar PowerShell-like cmdlet style functions, like Get-MyAppRecords or whatever). Writing a module also makes it easier to use the underlying .Net objects to make the web requests and do JSON conversion, that way you don't have a dependency on PowerShell 3.0 if your environment is still heavily locked on 2.0.

I wasn't going to post about this here for a while since I'm not all that close to a releasable version, but I am working on a project that would simplify some of this. It will be on GitHub when it's ready.

Adbot
ADBOT LOVES YOU

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Briantist posted:

Well the two languages have entirely different purposes. I mean really, they aren't even both languages. PowerShell is a scripting language/shell environment. RoR is an MVC web platform (rails is the platform, and that's what's abstracting the SQL stuff, Ruby is the language).

Before I learned PowerShell I was using Ruby (not rails) for most of my windows scripting, and using SQL with it is just as painful.

But I get what you're saying; it feels wrong to use SQL directly in scripts, and it kind of is.

My approach to this is usually to separate the concerns. Lately, I've been fond of wrapping SQL databases in a REST API, using an ASP.NET Web API project, with something like Entity Framework as an ORM. This is in C#.

Then a server runs IIS and hosts that app, and in PowerShell I either call the REST API directly (Invoke-RestMethod), or even better, I write a PowerShell module that wraps the API, and then provide familiar PowerShell-like cmdlet style functions, like Get-MyAppRecords or whatever). Writing a module also makes it easier to use the underlying .Net objects to make the web requests and do JSON conversion, that way you don't have a dependency on PowerShell 3.0 if your environment is still heavily locked on 2.0.

I wasn't going to post about this here for a while since I'm not all that close to a releasable version, but I am working on a project that would simplify some of this. It will be on GitHub when it's ready.

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!

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