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
stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
I'm trying to improve the way we're doing some things with Powershell on a project I'm working on, particularly working with credentials. I've been doing some experimentation with the PSCredential object type, more or less using the method described here. Everything seems to work pretty well. The script I want to use this in gets executed from a variety of machines, but always as the same domain user. I copied the encrypted password file I created to a different server, but when I executed
code:
Get-Content secretfile.txt | ConvertTo-SecureString
I got an error reading "ConvertTo-SecureString : Key not valid for use in specified state." Can I not take an encrypted file from server to server like this, or am I just doing something wrong?

Adbot
ADBOT LOVES YOU

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

adaz posted:

Correct, the key it generates is machine specific. If you need to run the script on multiple machines this is a decent enough workaround (to be honest, I've forgotten how I have worked around this in the past): http://powertoe.wordpress.com/2011/06/05/storing-passwords-to-disk-in-powershell-with-machine-based-encryption/

Thanks for the link, that's an interesting approach. I only have a few different servers to work with, so having to create a separate password file is more annoying than anything else. I think I'll just do it that way, I was really just wanting done confirmation before I wasted any more time trying to figure out why it wasn't working.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

Wicaeed posted:

What's the easiest way to break out of a scriptblock in Powershell? I have a script that will generate quite a few errors if a variable is not set, which can happen if it is a certain date.

Can I add a simple If/Else statement to say something like:

code:
If ($variable -eq $null){
Goto next function
Else{
do something here
}
Edit: Hurr, a simple 'break' will work there. Another question regarding variables.

It's purely a matter of style, but I find it cleaner to do something like
code:
If ($variable -ne $null){
{
do something here
}
Something about an empty if block just bugs me.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

RICHUNCLEPENNYBAGS posted:

So does Powershell just lack shortcuts to jump around text, or do I just not know them, or what? Like in bash you have a ton of shortcuts like ^a for the beginning of the line, alt-f to go forward one word, ^w to delete a word, etc, but apparently you just have to hold down the arrow button forever to do this kind of stuff with Powershell which is annoying.

Try home and end. Ctrl -> and ctrl <- will go one word at a time. That's not just powershell though, that's standard Windows behavior.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

GPF posted:

And, Phone, Import-CSV and Get-Content were brought to us by $deity itself.

Yeah, the ease of reading from and writing to files is really nice. The more I use it, the more I like it to be honest. A project I'm working on is using a shitton of legacy VBScript, and I wish they would rework this poo poo to use PS instead for a variety of reasons. The biggest one being that the cscript/wscript engine has a totally sexy bug where it may not always return the exit code you specify in your script to the calling process.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
Getting an unexpected error with a script I've used successfully on other servers without any problems:

code:
$password =  ConvertTo-SecureString "fakepassword" -AsPlainText -Force
ConvertFrom-SecureString $password | Set-Content c:\passwordfile.txt
(I know, -AsPlainText is not ideal, but for various reasons I can't do this interactively)

Anyway, I get the following error:

ConvertFrom-SecureString: The system cannot find the file specified

Some googling implies that this might be a UAC thing, can anyone confirm? I don't think it's related to c:\passwordfile.txt, since I would expect an error to be thrown on Set-Content in that case.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
I'm sorry, I was a little extreme in my script sanitizing. I'm writing my file to a directory, not the the root. The path definitely exists, but regardless if it were an issue creating that file wouldn't the error get thrown on Set-Content? I have some additional error text too that will probably be helpful, but I had to jump back on a vpn which blocks sa. I'll try to remember to post later.

e: Just realized I can ssh to home and get to the forums that way. Here's the full output:


code:
At C:\temp\f40ee6ff-e305-417d-9aac-6b2221edaa8c.ps1:3 char:25

+ ConvertFrom-SecureString <<<<  $password | Set-Content C:\myDirectory\credentials.txt
    + CategoryInfo          : NotSpecified: (:) [ConvertFrom-SecureString], CryptographicException
    + FullyQualifiedErrorId : System.Security.Cryptography.CryptographicException,Microsoft.PowerShell.Commands.ConvertFromSecureStringCommand

stubblyhead fucked around with this message at 18:39 on Mar 15, 2012

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

adaz posted:

SecureString and convert-SecureString requires you to use the same user account to decrypt the password as you used to encrypt it. Are you encrypting the save file with another user account then running it on the server under a different account?

This is the script which is going to create that encrypted file to be used later; there's no decryption happening at all in this script. The very same script has worked without error on other servers, but I have limited visibility to see what kind of settings might be different on this one compared to those.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

adaz posted:

If that's the case I'm guessing it is going to be something more esoteric, perhaps a different version of .NET? Are the servers you're having the issue on older/newer/have anything at all in common? Does your account not have admin permissions on those servers?

I'm going to go with 'esoteric' as well. The account I'm using is an enterprise admin, so it should be able to do anything it wants on that server. That's one of the reasons I can't do this interactively actually--I'm not permitted to log in to the remote server with my personal account, and I can only use that admin account in automation scripts and crap like that. So basically I have the whole thing wrapped up into a process that runs powershell on the remote server (authenticating as the admin user) and executes the script.

It's a moot point anyway though. I finally just asked one of my colleagues with greater access to do it for me. Same script worked perfectly logged into the server as that user, so I don't know what the gently caress. Hopefully I'll be able to actually read it as a secure string later on.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

adaz posted:

Is your account actually blocked from logging into the server via GPO or something not just like a IT policy? If that's the case that would explain it. The secure cmdlets actually require a profile to be created and for the given user to "log onto" the machine before they will work. I ran into it before when doing scheduled tasks with cmdlets and secure strings, since the profile wasn't loaded I got a generic cryptographic error.

If this is the case it won't be able to decrypt it when the script runs as the account trying to do the decryption still wouldn't be able to log on to use the cryptographic provider on the machine.

Hmm, interesting... my personal account is not in the remote users for that machine, but the service account is--I'm just barred from using it that way by IT policy. I'm spawning that powershell process in such a way that the target server doesn't even know my personal account exists, but it's entirely possible that the service account didn't have a profile on this server. I was able to decrypt that file and use it in a credentials object though. My friend remoted in using the service account, so if it was a matter of the profile not existing that would have fixed it.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

Thrawn200 posted:

I get an error saying that "The term 'C:\whatever\$path\is' is not recognized as the name of a cmdlet, function, script file, or operable program."

Is this a typo on your part, or is that actually in the error message?


adaz posted:

Also the equivalents to most of that stuff you're talking about in batch files can be found by doing a cd ENV: and then a dir, just as a quick and easy reference although there are plenty more. So ENV:SystemDrive is the system dir.

Neat, I didn't know you could do this. Have a technet link or something for some further reading?

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

adaz posted:

To get started what I'd use is a function I wrote long ago and my normal method of avoiding export-CSV's... eccentricities. Yes it would be a glorious world where we could just pipe the DataSet to export-csv and it'd handle it gracefully... unfortunately that world doesn't exist.

Sorry to quote from so far back, but good lord you are not kidding about this cmdlet. I have a multi-line comma-delimited string, you stupid piece of poo poo! Why won't you let me just pipe it straight in!

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

Jelmylicious posted:

If it is already comma delimited, just pipe it to a text file with .csv as its extension.

welp guess that would have worked too. :toot: I figured out a lot of poo poo in the process of getting this to work though, so it wasn't a total wash.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

adaz posted:

This might or might not work depending on the encoding parameters and the parser, I've had issues with it before. IN general I find it easier to use the creating your own object approach.

But yeah, export-csv :negative:

Yeah the comments in your code from a few pages back were really helpful. I was using a script one of my colleagues wrote as a template, and he did it in basically the same way. He didn't comment his code though, so I had no idea what he was doing and subsequently left out some pretty important stuff in my own script.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

adaz posted:

As a note, and I missed this last week, but the beta of powershell 3.0 is out: http://www.microsoft.com/download/en/details.aspx?id=28998

Has the public beta for Powershell 3 been discontinued? This link is dead now.

e: n/m, found a new link http://www.microsoft.com/en-us/download/details.aspx?id=29939

stubblyhead fucked around with this message at 04:25 on Jun 9, 2012

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

adaz posted:

Negatives:
Remoting is still balls bad if you need to cross forests that have no trust, as in so complex the lead architect was trying to get it working before their presentation and couldn't.

Export-CSV remains dumb :argh:

It also inserts spaces instead of tab characters, which bothers me to no end. I really don't want to bring back the Great Indent Wars though, so I'll say no more about it.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

kampy posted:

Yeah, kind of. Usedefaultcredentials is false by default, so you'll need to set it to true.

code:
$req = [System.Net.HttpWebRequest]::Create("https://companyweb.whatever")
$req.UseDefaultCredentials = $true
$res = $req.GetResponse()

Correct, see here. To be clear, this will use the same credentials as whatever user is running the script, so make sure whatever is running it (scheduled task, background service, whatever) has the authorization to do what's necessary.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

Drumstick posted:

Im not as familiar with using powershell in that way kampy. It seems like every so often a problem comes up and I know powershell is capable of handling it in a much easier fashion. I have a hard time distinguishing when to use that form(?) over the one I posted.

As an object lesson, let's look at the example kampy used. Look at the online help for Get-ADUser (Get-Help Get-ADUser -full). Just before the list of examples, it says its output type is Microsoft.ActiveDirectory.Management.ADUser. If there's a lot of these, it'll just be an array of ADUser objects instead of only one.

Now look at the help for Add-ADPrincipalGroupMembership. Under the -Identity parameter, ADUser is listed as a valid input type. It also says that it accepts pipeline input, which means we can run the first command, pipe it into the second, and it will automatically act on each object one at a time.

Functionally there's no difference between doing
code:
$users = Get-ADUser -Filter * -Searchbase "foo"
Foreach ($user in $users) {
  Add-ADPrincipalGroupMembership -MemberOf "bar" -Identity $user
}
and what kampy did; kampy's way is just more concise and easier to read once you get your legs under you. Let's pretend for a second that we wanted to add a single user into a bunch of groups, though. The -MemberOf parm doesn't take pipeline input, so you would need to do a foreach or similar to add the user to all your groups.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

kampy posted:

Both ways work identically when adding users from an OU to a group as long as none of the users is a member of said group. However if one of the users fetched by Get-ADUser is already a member of the group, none of the users will get added when passing the user objects through a pipeline.

Are you sure that is correct? The way I understand it, only one object gets passed from the pipeline at a time, so the cmdlet wouldn't have any notion that there were other objects, much less that some were already in the security group. It doesn't look like -Identity even accepts an array as input.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

stubblyhead posted:

Are you sure that is correct? The way I understand it, only one object gets passed from the pipeline at a time, so the cmdlet wouldn't have any notion that there were other objects, much less that some were already in the security group. It doesn't look like -Identity even accepts an array as input.

OK, a little more detail now that I've had a chance to experiment and refresh my memory a bit. Passing an array in by pipeline will hit the function all at once, but it will run the PROCESS block once for each object instead of only a single time if it were passed as a parameter. Take the following:
code:
function Do-Stuff {
    [cmdletbinding()]
    param(
        [Parameter (ValueFromPipeline = $True)]
        [string]$things)

    BEGIN{
        Write-Output "begin block"
    }

    PROCESS{
        Write-Output "process block"
        foreach ($thing in $things) {
            Write-Output $thing
        }
    }

    END{
        Write-Output "end block"
    }
}
If you ran this as "a","b","c" | Do-Stuff, you'd get out

begin block
process block
a
process block
b
process block
c
end block

Trying to do Do-Stuff -things "a","b","c" would throw an error because it can't take an array of strings in that parameter. If we did allow that, our output would be
begin block
process block
a
b
c
end block

I guess it is possible that the cmdlet has some functionality in the BEGIN block that would halt if any of the users were already in the target group, but that would be a really bizarre thing to do IMO.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

FISHMANPET posted:

I'll see if I can give it a try, as long as I don't need to put it into DNS, because AD doesn't control DNS, we have to go to the dark overlords to get SRV records put in.

It can be installed on 2008 also, so if you have a sandbox environment already you could just put it on there.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
Am I going about things in entirely the wrong way, or is handling HTTP requests a very cumbersome process in Powershell? For instance, if I wanted to get google's homepage, I could do something like this:

code:
$Request = [System.Net.WebRequest]::Create("http://www.google.com")
$Request.Method = "Get"
$Response = $Request.GetResponse()
$Stream = $Response.GetResponseStream()
$Reader = New-Object System.IO.StreamReader $Stream;
$Reader.ReadToEnd()
Is there really no simpler way to do this?

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

kampy posted:

That's pretty much the way it goes in PowerShell v2, in v3 you can use Invoke-WebRequest http://www.google.com

Good thing I'm using PS 3.0! :v: This is about 1000% simpler, thanks for the tip!

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

Swink posted:

Anyone else getting a shitload of yellow warning errors with 3.0? I'm getting them with every command. Although it might be the exchange2010 snapin I'm using.

Also, half of the commands dont have a help entry, and update-help doesnt work. Running as Admin or otherwise.

I haven't had any problems with warnings myself. I have had some issues with Get-Help though, mostly that it wants to download updates to it frequently. It hasn't done it for a while though, but when I first installed it it would try the first time I'd use it in a session.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

kampy posted:

I don't have the exchange cmdlets installed, but you might want to consider using select and Export-Csv there instead of format-table, something like this should work:

code:
get-distributiongroupmember -identity "groupname" | select Name,PrimarySMTPAddress | Export-Csv -Path nameslist.csv -NoTypeInformation

Yeah, Import-csv is a POS, but export works pretty well. You don't need to do that sort of redirection in powershell.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

Wicaeed posted:

So this is kind of a stupid question:

Company has a need to sign executable files with a cert and time stamp from verisign.

This is handled by a relatively simple command line command, but it requires the use of a Windows SKD CMD Shell.

Ideally, I would like to create a script that I can use to simply drag an executable to be signed over, and have poweshell work it's magic.

I'm having a devil of a time finding out how to really DO this, however.

Tips/Hints?

I'm pretty sure dropping things like that is equivalent to running
code:
c:\scripts\myScript.ps1 c:\files\myfile.exe
I'm not sure if that really answers your question though, but if you can write a script that'll do whatever needs to be done using that syntax on the command line, you should be good.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
A question about error handling for you guys. I'm working with the vmWare powerCLI, but the concepts I'm wondering about should be universal. So I have some try-catch blocks kind of like this:

code:
try {
  $datastore = $vmhost | get-datastore | where-object { some conditions } | get-random
}
catch {
  write-error $Error[0]
  Exit 1
}
I was working under the faulty assumption that the line in the try block would throw an error if there were no objects satisfying the condition, but it just sets $datastore to null which makes things blow up later on.

I was planning to just add something like this to the try block:
code:
if ($datastore -eq $Null) {
  throw "no such datastore"
}
Is this my best option, or is there a better way of doing this?

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

-Dethstryk- posted:

Does anyone know the optimal (if any) way I could store date/time information in Excel's date-time code, so that when I write out CSV's that for log script utilities I can just open them up and Excel can easily know what they are? If I didn't need the time I could just import the data pretty easily, but I can't figure out a way to import the time part of it.

Edit: Just so I'm clear, the format I'm referencing is Excel's that looks like this: 41284.7083333333 is January 10, 2013 5:00pm.

If you have it as a DateTime object you can use the ToOADDate() method to convert it.

code:
PS > $now = get-date

PS > $now.ToOADate()
41284.7292422801

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
How would you guys handle this kind of situation? I want to randomly arrange a collection. I can do it like this:
code:
$random = Get-Random -InputObject $myCollection -Count $myCollection.Length
but it doesn't work if $myCollection only has a single element. I'm not sure if this is how the PowerCLI cmdlets are written or if it's just how Powershell works, but the Get-Cluster cmdlet will return a single object if there's only one that matches the criteria, and a collection of them if there's multiple. The Cluster object doesn't have a Length property, so Get-Random thrown an error because the parameter gets passed a null value. I'd prefer not to do a check on the length before doing this if possible. Is there any way I can make it handle a single object as a collection with length 1, or is there just a better way to approach this altogether?

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
Thanks, will try this out tomorrow.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
What you want to do use are Get-ACL and Set-ACL. Take some directory that has permissions the way you want them, and do something like
code:
$myACL = Get-ACL -path <reference path>
Set-ACL -path <new directory> -AclObject $myACL
I would imagine it's possible to be more granular with this (i.e. add permission x for AD object y), but it's not something I've messed around with myself. Also, instead of mixing DOS commands with PS (though that is totally allowed) you can do this to create your new directory:
code:
$name = read-host "Enter Company Name"
New-Item  -path c:\Users\test\Desktop\ -name $name -itemtype directory

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
I need to do some remote admin on an AWS instance using powershell, but the machine I need to do it from can only access the web through a proxy server. Assuming the AWS server is configured correctly am I correct in thinking that all I need to do is specify the proxy details with New-PSSessionOption and feed that into New-PSSession?

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
Are you using an elevated prompt?

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

Briantist posted:

I've never tried it before, but yeah that's how it should work. You could also use the PSSessionOption object in Invoke-Command or Enter-PSSession, and you can set the $PSSessionOption variable to set the default options going forward.

I'm not sure if I'm doing the session settings wrong or if it's AWS fuckery getting in my way. If I'm reading it right winrm is only listening on its private IP addresses, and indeed the public IP isn't even listed for any of the adapters. I'm guessing Amazon NATs that out or something, but regardless the winrm service doesn't seem to be accessible from the internet at large.

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe
It wasn't the Windows firewall, but you're on the right tack. When you start AWS instances you assign security groups that specify what kind of traffic you want to allow, and the group I used for my test server didn't have the right ports open. A couple quick changes and I can get in.

e: Actually I spoke too soon. I am able to connect directly to the instance, but going through a proxy appears to require https. A certificate is required to start an https listener, and I'm not sure a self-signed one will pass muster with my client (no CA in AWS we can use, and setting one up will probably get shot down as well). I think this is becoming more of an AWS question than a PS question, so I think I'll bow out at this point.

stubblyhead fucked around with this message at 23:00 on Jul 1, 2015

Adbot
ADBOT LOVES YOU

stubblyhead
Sep 13, 2007

That is treason, Johnny!

Fun Shoe

stubblyhead posted:

e: Actually I spoke too soon. I am able to connect directly to the instance, but going through a proxy appears to require https. A certificate is required to start an https listener, and I'm not sure a self-signed one will pass muster with my client (no CA in AWS we can use, and setting one up will probably get shot down as well). I think this is becoming more of an AWS question than a PS question, so I think I'll bow out at this point.

In the unlikely event anyone cares, the client didn't give a poo poo about self-signed certificates since these are short-lived servers by design. I actually just copied the Remote Desktop cert into Personal and skipped CA and CN checks. I hit a minor roadblock due to their proxy being a butthead, but switching the WinRM service to listen on 443 instead of 5986 took care of that. The powershell part to this was actually really simple, it was all the other layers that caused problems.

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