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
Scaramouche
Mar 26, 2001

SPACE FACE! SPACE FACE!

myron cope posted:

It's a Windows 8.1 machine. It's not a huge deal, but I don't need to see the output so if it's possible to hide I would.

We do have another pc that would be better anyway, maybe I should just move it to that one.

I just tried the same setup on Windows 7 and I think Braintist is right; there was no popup and I didn't bother messing with a window style.

Adbot
ADBOT LOVES YOU

myron cope
Apr 21, 2009

Scaramouche posted:

I just tried the same setup on Windows 7 and I think Braintist is right; there was no popup and I didn't bother messing with a window style.

I think that worked, although I did both. Thanks!

MF_James
May 8, 2008
I CANNOT HANDLE BEING CALLED OUT ON MY DUMBASS OPINIONS ABOUT ANTI-VIRUS AND SECURITY. I REALLY LIKE TO THINK THAT I KNOW THINGS HERE

INSTEAD I AM GOING TO WHINE ABOUT IT IN OTHER THREADS SO MY OPINION CAN FEEL VALIDATED IN AN ECHO CHAMBER I LIKE

Powershell total noob here (also new to scripting in general yay)

What I'm trying to do is import a CSV, with user input not a predefined file/location, the CSV will have 3 columns with headers (it might change and have no headers but I know how to fix that). The headers are Location,Region,Division. There will likely be a few hundred rows that I have to step through.

code:
import-module ActiveDirectory
Import-CSV -prompt | foreach-object
{
	#utilizing location to get full computer name#
	$Store = "STORE"+$_.Location 
	$Host = "HOST"+$_.Location
	
	#Utilizing full computer name to get object#
	$StoreDN = (Get-ADUser -identity $Store)
	$HostDN = (Get-ADUser -identity $Host)
	$targetOU = (ou=$_.region, ou=$_.division, ou=PROD, ou=Stores, DC=my.domain.name)
		
		Move-ADObject -identity $StoreDN -TargetPath $TargetOU
		Move-ADObject -identity $HostDN -TargetPath $TargetOU
}
So, each location has 2 objects associated with it, there is a prefix that identifies between the 2, the prefixes are HOST/GUEST, both objects will move to the same region/division, and all objets will move to the location path - domain/stores/prod/division/region region being the lowest level OU. Region/division will change based on whatever location we are dealing with, hence the need for region/division in the CSV columns.

Am I headed down the right path here? We are also looking at doing some sort of logging for success/failure, how would I go about doing that?

MF_James fucked around with this message at 21:58 on Jan 22, 2015

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

MF_James posted:

Powershell total noob here (also new to scripting in general yay)

What I'm trying to do is import a CSV, with user input not a predefined file/location, the CSV will have 3 columns with headers (it might change and have no headers but I know how to fix that). The headers are Location,Region,Division. There will likely be a few hundred rows that I have to step through.

code:
import-module ActiveDirectory
Import-CSV -prompt | foreach-object
{
	#utilizing location to get full computer name#
	$Store = "STORE"+$_.Location 
	$Host = "HOST"+$_.Location
	
	#Utilizing full computer name to get object#
	$StoreDN = (Get-ADUser -identity $Store)
	$HostDN = (Get-ADUser -identity $Host)
	$targetOU = (ou=$_.region, ou=$_.division, ou=PROD, ou=Stores, DC=my.domain.name)
		
		Move-ADObject -identity $StoreDN -TargetPath $TargetOU
		Move-ADObject -identity $HostDN -TargetPath $TargetOU
}
So, each location has 2 objects associated with it, there is a prefix that identifies between the 2, the prefixes are HOST/GUEST, both objects will move to the same region/division, and all objets will move to the location path - domain/stores/prod/division/region region being the lowest level OU. Region/division will change based on whatever location we are dealing with, hence the need for region/division in the CSV columns.

Am I headed down the right path here? We are also looking at doing some sort of logging for success/failure, how would I go about doing that?

I have a few points:

  • Import-CSV doesn't take a "-Prompt" parameter. You can handle this a few different ways. You can use Read-Host to get user input which might be the easiest to understand if you're new to this:
    code:
    $csv = Read-Host -Prompt 'Where is the CSV?!'
    Import-Csv -Path $csv
    
    For bonus points, use Test-Path to make sure the file given by the user actually exists.

    What I'd prefer instead is to make the script itself take a -Path parameter, and make it required. If the user doesn't supply a path when calling the script, Powershell will prompt for you. To do that, you'd add stuff like this to the top of the script:
    code:
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Enter the path to the CSV file.'
        )]
        [ValidateScript( { $_ | Test-Path -PathType Leaf } )]
        [String]
        $Path
    )
    
    # your code here
    
    $Path | Import-Csv | ForEach-Object {
        # ...
    }
    
    # your code here
    
    This code also makes sure whatever the user enters exists on the file system and is a file (Leaf) rather than a folder. It's a lot more elegant and it's the "powershell way" of doing it, but it can be tough to understand if you're new to both this language and scripting itself.

  • If you're trying to retrieve a computer object from AD, you should probably use Get-ADComputer rather than Get-ADUser.
  • Your assignment of $targetOU is at best creating an array, but is probably invalid as written. Try this:
    code:
    $targetOU = "ou=$($_.region),ou=$($_.division),ou=PROD,ou=Stores,DC=my.domain.name"
    
    You can use variables directly in a double quoted string, but if you're trying to access a property with dot notaton it won't work. $(...) is a sub-expression and can be used in strings. Inside the parentheses you can use variables, cmdlets, entire pipelines. The result of all the code is the result of the sub-expression and will be put into the string.
  • Regarding building the $targetOU string, also just be careful that your regions and divisions don't contain commas. If they do, you need to escape them with \, (for AD's sake, not powershell).
  • Your variable names for $StoreDN and $HostDN imply that you think you'll get a DN as a string in response from Get-ADUser (or Get-ADComputer). You will in fact receive a complete object. It doesn't matter that you called the variables what you did, but you should be aware of what you'll have.
For the most part though, yeah it seems that you are headed down the right path.

For catching errors, you probably want to wrap some of your code in try/catch blocks, which can be a bit daunting for a beginner. This kind of seems like a one-shot type of task rather than something you'll be running a whole lot. Is that accurate? If so, you might just want to step through the code in Powershell ISE bit by bit. Here's a simple try/catch you could do:
code:
Import-CSV -prompt | foreach-object
{
	#utilizing location to get full computer name#
	$Store = "STORE"+$_.Location 
	$Host = "HOST"+$_.Location
	
	#Utilizing full computer name to get object#
      try {
	$StoreDN = (Get-ADUser -identity $Store)
	$HostDN = (Get-ADUser -identity $Host)
	$targetOU = (ou=$_.region, ou=$_.division, ou=PROD, ou=Stores, DC=my.domain.name)
		
		Move-ADObject -identity $StoreDN -TargetPath $TargetOU
		Move-ADObject -identity $HostDN -TargetPath $TargetOU
      } catch {
        $_   # set a breakpoint here so the whole script stops and you can see what happened?
      }
}

MF_James
May 8, 2008
I CANNOT HANDLE BEING CALLED OUT ON MY DUMBASS OPINIONS ABOUT ANTI-VIRUS AND SECURITY. I REALLY LIKE TO THINK THAT I KNOW THINGS HERE

INSTEAD I AM GOING TO WHINE ABOUT IT IN OTHER THREADS SO MY OPINION CAN FEEL VALIDATED IN AN ECHO CHAMBER I LIKE

Thanks for taking a look.

Yes this is a one off script (I will take it with me and make it better in the event I need to use it elsewhere in my career)

I didn't really know how to prompt the user and my googling wasn't turning up much, the user will be me in this case, but I'll be running the script a few times with multiple files and I feel it's easier to deal with it by manually inputting than figuring out how to do it completely automated especially consdering my point above.

I can't believe I didn't notice I had Get-ADUser instad of Get-ADComputer.

Yeah I named the variables a while ago and realize it's not the DN I'm getting, but I don't see a need to change them at this point honestly.

With the $TargetOU, I'm guessing it's not exactly the best way to point to the OU that I want, but I found it slightly easier in my head to pass it as a variable string to the GET-ADobject than it was to put the whole structure into each Get. Thanks again for pointing out how that would essentially blow up how I had it written.

Spazz
Nov 17, 2005

Crossposting this poo poo in here because I'm about to set something on fire...

http://stackoverflow.com/questions/27975936/new-webserviceproxy-failing-to-authenticate-with-ntlm

I'm honestly wondering if I need to hack something together to make this stupid thing work.

Space Whale
Nov 6, 2014
My workplace has no DBA. It's basically been badly administrated by the back end devs and architect round robin. I'd rather not play "copy and paste poo poo into scripts in management studio" so I figure it's time to learn a powershell.

Coming into this as a back end dev, what are some good ways to avoid making stupid mistakes?

Also, in general, what's a good way to tell that you need to start brow beating management into getting a DBA in terms of time devs spend doing it, the schema sucking, or just the size of your company and the amount of data flying in? We're completely dependent on our data to make money or do business at all, so I'd think that alone would justify it, but we have some very heads-down back end devs who just do as told and nobody's made a stink yet.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Space Whale posted:

My workplace has no DBA. It's basically been badly administrated by the back end devs and architect round robin. I'd rather not play "copy and paste poo poo into scripts in management studio" so I figure it's time to learn a powershell.

Coming into this as a back end dev, what are some good ways to avoid making stupid mistakes?

Also, in general, what's a good way to tell that you need to start brow beating management into getting a DBA in terms of time devs spend doing it, the schema sucking, or just the size of your company and the amount of data flying in? We're completely dependent on our data to make money or do business at all, so I'd think that alone would justify it, but we have some very heads-down back end devs who just do as told and nobody's made a stink yet.

Are you a .NET shop? Use SSDT.

Space Whale
Nov 6, 2014

Ithaqua posted:

Are you a .NET shop? Use SSDT.

Yes.

Welp, learning new things is fun!

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Space Whale posted:

Yes.

Welp, learning new things is fun!

Yeah, then use SSDT to source control your schema. That's the thing you care about... change scripts are an artifact of transforming one version of the schema into another schema. They don't need to be source controlled as long as you have something that can analyze an existing database and generate a changescript. SSDT generates DACPACs, which are just a canonical version of your DB schema. You can publish a DACPAC to an existing database, which analyzes the differences, creates a change script, and executes it.

Integrate that into your release process and you're done.

Space Whale
Nov 6, 2014

Ithaqua posted:

Yeah, then use SSDT to source control your schema. That's the thing you care about... change scripts are an artifact of transforming one version of the schema into another schema. They don't need to be source controlled as long as you have something that can analyze an existing database and generate a changescript. SSDT generates DACPACs, which are just a canonical version of your DB schema. You can publish a DACPAC to an existing database, which analyzes the differences, creates a change script, and executes it.

Integrate that into your release process and you're done.

I'm not changing the schema right now, I'm working on the data. I'm merging and deleting entries.

Is there still something I should be doing besides backups of the data before and after?

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
This language is such garbage. Never have I dealt with a language where I have to spend 90% of my time just fighting the language because it doesn't behave rationally.

I'm trying to change the local admin password on a bunch of servers. For reasons I don't understand WMIC isn't working so I'm trying to use invoke-command to run a command on the remote machine. I want to pass my script a list of servers that I want it to change the password on, and then I want it to return a result of what happens on each server, either sucess or failure.

I keep wrapping things in try-catch blocsk and invoke-command just returns garbage as far as error handling goes. I want invoke-command to return an error, but the only way it will return an error is if the the computer you're running on doesn't exist. And it doesn't actually return an error that can be caught, I have to run it as a job and then do some fuckery to get an actual error out of it. If I want to get an error out of my script block that I'm running on the remote machine, that's a whole other can of worms. This doesn't seem that complicated, and one of those vaunted use cases that Powershell is supposedly great for. Automate a task on a bunch of servers, and let me know how it goes (is it the wanting to know how it goes part that I'm not supposed to be doing with powershell?)

I'm about ready to find something to treat as a physical manifestation of powershell, so I can throw it across the room because it's all garbage. I've been fighting all week with this and all Powershell does is get in my way. Maybe it's because I'm used to programming in other languages that actually make sense? Do I need a lobotomy to think in Powershell terms?

Here's my code, for reference, maybe I'm going about this entirely the wrong way?

code:
Param(
    [Parameter(Mandatory=$true)]
    [String[]]
    $ComputerNames,

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

$password = Read-Host -Prompt "Enter Password Base" -AsSecureString

$RemoteChangeScript = {
    Param(
        [Parameter(Mandatory=$true, Position=1)]
        [SecureString]
        $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)
            {
                Write-Warning "Server: '$($TargettedServerName)' has a local account '$($User)' whos password is NOT being changed by this script"
            }
        }

    $password.AppendChar($env:computername[-1].ToString().ToLower())
    $password.AppendChar($env:computername[-2].ToString().ToLower())
    try
    {
        $objUser = [ADSI]"WinNT://localhost/$($LocalAccount), user"
        $objUser.psbase.Invoke("SetPassword", [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)))
        $Properties = @{
            TargettedServerName = $TargettedServerName
            Username =  $LocalAccount
            RealServerName = $env:computername
            Error = "Sucess"
        }
    }
    catch
    {
        Write-Error "Error changing password for user:$($LocalAccount) on server:$($TargettedServerName)"
        $Properties = @{
            TargettedServerName = $TargettedServerName
            Username =  $LocalAccount
            RealServerName = $env:computername
            Error = $_.Exception.Message
        }

    }
    finally
    {
        
        $ReturnData = New-Object PSObject -Property $Properties
        $ReturnData
    }
}
    
    foreach ($Computer in $ComputerNames)
    {
        $Return = Invoke-Command -ScriptBlock $RemoteChangeScript -ArgumentList @($password, $LocalAccount, $Computer) -ComputerName $Computer -AsJob
        #$Return = Invoke-Command -ScriptBlock {echo "helo"} -ComputerName $Computer -AsJob
        try
        {
            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))

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin

FISHMANPET posted:

This doesn't seem that complicated, and one of those vaunted use cases that Powershell is supposedly great for.

I was just thinking that there HAS to be an easy way to do this but when I searched for it, it's apparently really complicated for reasons that I don't understand.

This is the kind of thing that there ought to be a commandlet for.

Fenrisulfr
Oct 14, 2012
Is there some reason you're not just doing this (modified for your other requirements, of course):

code:
$objUser = [ADSI]"WinNT://$computer/$user,user"
$objUser.SetPassword($password)
You shouldn't need to do any Invoke-Commands, the ADSI object can point to the server in its definition. You may need a "$objUser.SetInfo()" to push the password change but I'm not sure about that.

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
I don't think our firewall is allowing the ADSI connection through, but I can't really be sure because the error I get is not especially useful:
code:
The following exception occurred while retrieving member "SetPassword": "The network path was not found.
"
At line:1 char:1
+ $objuser.SetPassword("password")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], ExtendedTypeSystemException
    + FullyQualifiedErrorId : CatchFromBaseGetMember
And that's not really a useful error message, the machine clearly exists. I can't find any useful information on what this error means to be able to debug it.

Fenrisulfr
Oct 14, 2012

FISHMANPET posted:

I don't think our firewall is allowing the ADSI connection through, but I can't really be sure because the error I get is not especially useful:
code:
The following exception occurred while retrieving member "SetPassword": "The network path was not found.
"
At line:1 char:1
+ $objuser.SetPassword("password")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], ExtendedTypeSystemException
    + FullyQualifiedErrorId : CatchFromBaseGetMember
And that's not really a useful error message, the machine clearly exists. I can't find any useful information on what this error means to be able to debug it.

You may have already tried this, but you can a) check that your ADSI connection string is what you expect by writing it to output; and/or b) do a Test-Connection $computer to make sure that Powershell can connect to the server. If both of those look good, it may be a firewall issue like you said, or maybe Remote Management is disabled on those servers: https://msdn.microsoft.com/en-us/library/dd759202.aspx.

MF_James
May 8, 2008
I CANNOT HANDLE BEING CALLED OUT ON MY DUMBASS OPINIONS ABOUT ANTI-VIRUS AND SECURITY. I REALLY LIKE TO THINK THAT I KNOW THINGS HERE

INSTEAD I AM GOING TO WHINE ABOUT IT IN OTHER THREADS SO MY OPINION CAN FEEL VALIDATED IN AN ECHO CHAMBER I LIKE

Ok, so I've gotten very far in my move ad computer script after running it 100x and troubleshooting. I'm now a bit stuck with a mostly cosmetic issue.

code:
import-module ActiveDirectory
$Path = Read-Host -Prompt "CSV Location Please, Sir"
if ((Test-Path -path $Path))
{
$csv = Import-CSV $Path
	foreach($item in $csv)
	{
		$Store = "STORE"+$item.Location
		$VMHost = "HOST"+$item.Location
		$targetOU = "ou=" + $item.region + ",ou="+ $item.division + ",ou=PROD,ou=Stores,DC=qastr,DC=qapos,DC=bfrc"
		try{$teststore = Get-ADComputer -identity $Store}
		catch{$Store + "Could not be found" | Out-File C:\error.txt -Append}
		try{
			if(!$error){Get-ADComputer -identity $Store | Move-ADObject -TargetPath $TargetOU}
			else{$error.clear}
			}
		catch{$Store + "Could not be moved" | Out-File C:\error.txt -Append}
		try{$testhost = Get-ADComputer -identity $VMHost}
		catch{$VMHost  + "Could not be found" | Out-File C:\error.txt -Append}
		try{
			if(!$error){Get-ADComputer -identity $VMHost | Move-ADObject -TargetPath $TargetOU}
			else{$error.clear}
			}
		catch{$VMHost + "Could not be moved" | Out-File C:\error.txt -Append}
	}
}
else{Write-Output "The CSV path failed"}
So, the problem I'm having is that when you do a $error.clear it shits up the powershell window, so I wrapped them in an if/else so that if there's NOT an error, it should move the computer, if there was an error it should clear the error and move on to the catch which is outputting the computer name and telling me it couldn't find it or it couldn't move it depending on where it is in the script. The problem I'm having is that it's ALWAYS clearing $error, since I'm running this against 4400+ objects... that's a lot of making GBS threads all over my window that I don't want, does anyone know WHY my if/else isn't functioning properly, or is it something else about how $error.clear works that I don't understand?

MF_James fucked around with this message at 19:22 on Jan 29, 2015

Zaepho
Oct 31, 2013

MF_James posted:

So, the problem I'm having is that when you do a $error.clear it shits up the powershell window, so I wrapped them in an if/else so that if there's NOT an error, it should move the computer, if there was an error it should clear the error and move on to the catch which is outputting the computer name and telling me it couldn't find it or it couldn't move it depending on where it is in the script. The problem I'm having is that it's ALWAYS clearing $error, since I'm running this against 4400+ objects... that's a lot of making GBS threads all over my window that I don't want, does anyone know WHY my if/else isn't functioning properly, or is it something else about how $error.clear works that I don't understand?

Pipe the result of $error.clear to null
code:
else {
     $Error.Clear | out-null
}

MF_James
May 8, 2008
I CANNOT HANDLE BEING CALLED OUT ON MY DUMBASS OPINIONS ABOUT ANTI-VIRUS AND SECURITY. I REALLY LIKE TO THINK THAT I KNOW THINGS HERE

INSTEAD I AM GOING TO WHINE ABOUT IT IN OTHER THREADS SO MY OPINION CAN FEEL VALIDATED IN AN ECHO CHAMBER I LIKE

Zaepho posted:

Pipe the result of $error.clear to null
code:
else {
     $Error.Clear | out-null
}

Cool, that works, but I'm curious, isn't this just circumventing the issue at hand? It's causing it to nullify the output so I don't see it, but my initial oddity is still there, why is it running BOTH the if and the else? It should run the if (provided !$error returns true) and then, provided if/else works the same as it does in C++/C# and follows any sort of sense, it should NOT run the else statement.

Spazz
Nov 17, 2005

FYI it's $error.clear() to silently flush it.

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

FISHMANPET posted:

This language is such garbage. Never have I dealt with a language where I have to spend 90% of my time just fighting the language because it doesn't behave rationally.

I'm trying to change the local admin password on a bunch of servers. For reasons I don't understand WMIC isn't working so I'm trying to use invoke-command to run a command on the remote machine. I want to pass my script a list of servers that I want it to change the password on, and then I want it to return a result of what happens on each server, either sucess or failure.

I keep wrapping things in try-catch blocsk and invoke-command just returns garbage as far as error handling goes. I want invoke-command to return an error, but the only way it will return an error is if the the computer you're running on doesn't exist. And it doesn't actually return an error that can be caught, I have to run it as a job and then do some fuckery to get an actual error out of it. If I want to get an error out of my script block that I'm running on the remote machine, that's a whole other can of worms. This doesn't seem that complicated, and one of those vaunted use cases that Powershell is supposedly great for. Automate a task on a bunch of servers, and let me know how it goes (is it the wanting to know how it goes part that I'm not supposed to be doing with powershell?)

I'm about ready to find something to treat as a physical manifestation of powershell, so I can throw it across the room because it's all garbage. I've been fighting all week with this and all Powershell does is get in my way. Maybe it's because I'm used to programming in other languages that actually make sense? Do I need a lobotomy to think in Powershell terms?

Here's my code, for reference, maybe I'm going about this entirely the wrong way?

code:
# <snip>
    try
    {
        $objUser = [ADSI]"WinNT://localhost/$($LocalAccount), user"
        $objUser.psbase.Invoke("SetPassword", [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)))
        $Properties = @{
            TargettedServerName = $TargettedServerName
            Username =  $LocalAccount
            RealServerName = $env:computername
            Error = $null
        }
    }
    catch
    {
        Write-Error "Error changing password for user:$($LocalAccount) on server:$($TargettedServerName)"
        $Properties = @{
            TargettedServerName = $TargettedServerName
            Username =  $LocalAccount
            RealServerName = $env:computername
            Error = $_     #.Exception.Message
        }
    }
    finally
    {
        
        $ReturnData = New-Object PSObject -Property $Properties
        $ReturnData
    }
}
# <snip>

    
    foreach ($Computer in $ComputerNames)
    {
        $Return = Invoke-Command -ScriptBlock $RemoteChangeScript -ArgumentList @($password, $LocalAccount, $Computer) -ComputerName $Computer 
        #$Return = Invoke-Command -ScriptBlock {echo "helo"} -ComputerName $Computer -AsJob
        try
        {
            Write-Warning "$computer try"
            # $Result = Receive-Job $Return -ErrorAction Stop
            #Write-Warning $result
            # Write-Warning "hello this is the try block"
            #Write-Warning $result

	    if ($Return.Error) {
		throw $Return.Error
	    }
        }
        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))
What about something like this?

I took out -AsJob.

You're already returning an object which is good, and it already has an error property, even better.

As you pointed out, you can't throw an exception on the remote side and catch it on the local side. That's because it's a completely different session or runspace. Any objects sent back are serialized and so are no longer "live".

So I changed the code a bit so that when there is no error, it just sets the error property in your object to $null. When there is an exception, just use $_ and don't bother with the internal exception's message (right now we just want to pass this back to the caller).

Then back on the calling side, you check for an error in the property. At this point, you could JUST use this method (an if statement) instead of try/catch, but I'm assuming you want to use try/catch so if the error exists, then we just throw the error as is (which you can then catch and handle as necessary).

I haven't tested this yet, but it's fairly workable and much easier than using jobs.

Jobs give the added benefit of asynchronous-ness but your code as is could easily fail if the remote job were not yet completed. So you'd need more code to check on the status of the jobs that are waiting and receive the results when necessary.

Zaepho
Oct 31, 2013

MF_James posted:

Cool, that works, but I'm curious, isn't this just circumventing the issue at hand? It's causing it to nullify the output so I don't see it, but my initial oddity is still there, why is it running BOTH the if and the else? It should run the if (provided !$error returns true) and then, provided if/else works the same as it does in C++/C# and follows any sort of sense, it should NOT run the else statement.

i just noticed you have your if all contained on a single line and then a line break and then the else. try:

code:
if (stuff) {
     Do things
} else {
    Do Other Stuff
}


I'm betting the line break is confusing it.

Spazz posted:

FYI it's $error.clear() to silently flush it.

I was wondering if the method didn't need the () to run but I wasn't going to mess with code that was working without trying it myself or knowing for sure.

MF_James
May 8, 2008
I CANNOT HANDLE BEING CALLED OUT ON MY DUMBASS OPINIONS ABOUT ANTI-VIRUS AND SECURITY. I REALLY LIKE TO THINK THAT I KNOW THINGS HERE

INSTEAD I AM GOING TO WHINE ABOUT IT IN OTHER THREADS SO MY OPINION CAN FEEL VALIDATED IN AN ECHO CHAMBER I LIKE

I don't think it has to do with that. *EDIT* it doesn't, I changed the script to be "cleaner" for myself and the ifelse is all on a single line.

I noticed some odd behavior. If I open powershell and run the script immediately, it only runs $error.clear on the things I would expect it to (I have 7 cases where it will fail and 7 where it will succeed), so I get 7 $error.clear messages, when I run it a second time without closing/reopening the CLI I will get a clear message on all 14 cases, but the 7 that I expect to succeed still do succeed.

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

MF_James posted:

I don't think it has to do with that. *EDIT* it doesn't, I changed the script to be "cleaner" for myself and the ifelse is all on a single line.

I noticed some odd behavior. If I open powershell and run the script immediately, it only runs $error.clear on the things I would expect it to (I have 7 cases where it will fail and 7 where it will succeed), so I get 7 $error.clear messages, when I run it a second time without closing/reopening the CLI I will get a clear message on all 14 cases, but the 7 that I expect to succeed still do succeed.
Have you changed your to code use $Error.Clear() yet instead of just .Clear? You definitely need the parentheses.

Also, I'm curious why you're using $Error in the first place. It looks like you're doing it to catch errors at multiple points in the process so that you can tell which parts of the process failed. The better way to do this, I think, is to use nested try/catch blocks and/or to use multiple catch blocks that catch specific exceptions instead of just catching everything.

What if it looked like this:

code:
import-module ActiveDirectory
$Path = Read-Host -Prompt "CSV Location Please, Sir"
if ((Test-Path -path $Path))
{
$csv = Import-CSV $Path
	foreach($item in $csv)
	{
		$Store = "STORE"+$item.Location
		$VMHost = "HOST"+$item.Location
		$targetOU = "ou=" + $item.region + ",ou="+ $item.division + ",ou=PROD,ou=Stores,DC=qastr,DC=qapos,DC=bfrc"
		try {
			$teststore = Get-ADComputer -identity $Store
			try {
				Get-ADComputer -identity $Store | Move-ADObject -TargetPath $TargetOU
				try {
					$testhost = Get-ADComputer -identity $VMHost
					try {
						Get-ADComputer -identity $VMHost | Move-ADObject -TargetPath $TargetOU
					} catch {
						$VMHost + "Could not be moved" | Out-File C:\error.txt -Append
					}
				} catch {
					$VMHost  + "Could not be found" | Out-File C:\error.txt -Append
				}
			} catch {
				$Store + "Could not be moved" | Out-File C:\error.txt -Append
			}
		} catch {
			$Store + "Could not be found" | Out-File C:\error.txt -Append
		}
	}
}
else{Write-Output "The CSV path failed"}
So that's how it would look with nested try catch. Even better would be to catch specific exceptions:

code:
import-module ActiveDirectory
$Path = Read-Host -Prompt "CSV Location Please, Sir"
if ((Test-Path -path $Path))
{
$csv = Import-CSV $Path
	foreach($item in $csv)
	{
		$Store = "STORE"+$item.Location
		$VMHost = "HOST"+$item.Location
		$targetOU = "ou=" + $item.region + ",ou="+ $item.division + ",ou=PROD,ou=Stores,DC=qastr,DC=qapos,DC=bfrc"
		try {
			# $teststore = Get-ADComputer -identity $Store # Not needed anymore
			Get-ADComputer -identity $Store | Move-ADObject -TargetPath $TargetOU
			# $testhost = Get-ADComputer -identity $VMHost # Not needed anymore
			Get-ADComputer -identity $VMHost | Move-ADObject -TargetPath $TargetOU
		} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
			$Store + "Could not be found" | Out-File C:\error.txt -Append
		} catch [Microsoft.ActiveDirectory.Management.SomeMoveObjectException] { # I made this one up
			$Store + "Could not be moved" | Out-File C:\error.txt -Append
		} 
	}
}
else{Write-Output "The CSV path failed"}
This is much shorter, much cleaner. You'll just have to find the correct exception class for the Move-ADObject call. It also obviates the need for calling Get-ADComputer 4 times since you don't need to test the object first.

The only thing you might have to do is add -ErrorAction Stop onto both the Get- and Move- calls to ensure that the errors are terminating errors (and therefore will be caught by try/catch).

MF_James
May 8, 2008
I CANNOT HANDLE BEING CALLED OUT ON MY DUMBASS OPINIONS ABOUT ANTI-VIRUS AND SECURITY. I REALLY LIKE TO THINK THAT I KNOW THINGS HERE

INSTEAD I AM GOING TO WHINE ABOUT IT IN OTHER THREADS SO MY OPINION CAN FEEL VALIDATED IN AN ECHO CHAMBER I LIKE

Briantist posted:

Have you changed your to code use $Error.Clear() yet instead of just .Clear? You definitely need the parentheses.

Also, I'm curious why you're using $Error in the first place. It looks like you're doing it to catch errors at multiple points in the process so that you can tell which parts of the process failed. The better way to do this, I think, is to use nested try/catch blocks and/or to use multiple catch blocks that catch specific exceptions instead of just catching everything.

What if it looked like this:

code:
import-module ActiveDirectory
$Path = Read-Host -Prompt "CSV Location Please, Sir"
if ((Test-Path -path $Path))
{
$csv = Import-CSV $Path
	foreach($item in $csv)
	{
		$Store = "STORE"+$item.Location
		$VMHost = "HOST"+$item.Location
		$targetOU = "ou=" + $item.region + ",ou="+ $item.division + ",ou=PROD,ou=Stores,DC=qastr,DC=qapos,DC=bfrc"
		try {
			$teststore = Get-ADComputer -identity $Store
			try {
				Get-ADComputer -identity $Store | Move-ADObject -TargetPath $TargetOU
				try {
					$testhost = Get-ADComputer -identity $VMHost
					try {
						Get-ADComputer -identity $VMHost | Move-ADObject -TargetPath $TargetOU
					} catch {
						$VMHost + "Could not be moved" | Out-File C:\error.txt -Append
					}
				} catch {
					$VMHost  + "Could not be found" | Out-File C:\error.txt -Append
				}
			} catch {
				$Store + "Could not be moved" | Out-File C:\error.txt -Append
			}
		} catch {
			$Store + "Could not be found" | Out-File C:\error.txt -Append
		}
	}
}
else{Write-Output "The CSV path failed"}
So that's how it would look with nested try catch. Even better would be to catch specific exceptions:

code:
import-module ActiveDirectory
$Path = Read-Host -Prompt "CSV Location Please, Sir"
if ((Test-Path -path $Path))
{
$csv = Import-CSV $Path
	foreach($item in $csv)
	{
		$Store = "STORE"+$item.Location
		$VMHost = "HOST"+$item.Location
		$targetOU = "ou=" + $item.region + ",ou="+ $item.division + ",ou=PROD,ou=Stores,DC=qastr,DC=qapos,DC=bfrc"
		try {
			# $teststore = Get-ADComputer -identity $Store # Not needed anymore
			Get-ADComputer -identity $Store | Move-ADObject -TargetPath $TargetOU
			# $testhost = Get-ADComputer -identity $VMHost # Not needed anymore
			Get-ADComputer -identity $VMHost | Move-ADObject -TargetPath $TargetOU
		} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
			$Store + "Could not be found" | Out-File C:\error.txt -Append
		} catch [Microsoft.ActiveDirectory.Management.SomeMoveObjectException] { # I made this one up
			$Store + "Could not be moved" | Out-File C:\error.txt -Append
		} 
	}
}
else{Write-Output "The CSV path failed"}
This is much shorter, much cleaner. You'll just have to find the correct exception class for the Move-ADObject call. It also obviates the need for calling Get-ADComputer 4 times since you don't need to test the object first.

The only thing you might have to do is add -ErrorAction Stop onto both the Get- and Move- calls to ensure that the errors are terminating errors (and therefore will be caught by try/catch).

Honestly? I'm doing it this way because I don't know any better! Been figuring stuff out through googling and trial/error

Zaepho
Oct 31, 2013

Briantist posted:

Even better would be to catch specific exceptions:

code:
import-module ActiveDirectory
$Path = Read-Host -Prompt "CSV Location Please, Sir"
if ((Test-Path -path $Path))
{
$csv = Import-CSV $Path
	foreach($item in $csv)
	{
		$Store = "STORE"+$item.Location
		$VMHost = "HOST"+$item.Location
		$targetOU = "ou=" + $item.region + ",ou="+ $item.division + ",ou=PROD,ou=Stores,DC=qastr,DC=qapos,DC=bfrc"
		try {
			# $teststore = Get-ADComputer -identity $Store # Not needed anymore
			Get-ADComputer -identity $Store | Move-ADObject -TargetPath $TargetOU
			# $testhost = Get-ADComputer -identity $VMHost # Not needed anymore
			Get-ADComputer -identity $VMHost | Move-ADObject -TargetPath $TargetOU
		} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
			$Store + "Could not be found" | Out-File C:\error.txt -Append
		} catch [Microsoft.ActiveDirectory.Management.SomeMoveObjectException] { # I made this one up
			$Store + "Could not be moved" | Out-File C:\error.txt -Append
		} 
	}
}
else{Write-Output "The CSV path failed"}
This is much shorter, much cleaner. You'll just have to find the correct exception class for the Move-ADObject call. It also obviates the need for calling Get-ADComputer 4 times since you don't need to test the object first.

The only thing you might have to do is add -ErrorAction Stop onto both the Get- and Move- calls to ensure that the errors are terminating errors (and therefore will be caught by try/catch).

I like this one, my only alteration would be a final catch for everything else and I would log the exception message to make it easier to troubleshoot when things go wrong.
Specifically:
code:
$($_.Exception.Message)

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Zaepho posted:

I like this one, my only alteration would be a final catch for everything else and I would log the exception message to make it easier to troubleshoot when things go wrong.
Specifically:
code:
$($_.Exception.Message)
Yeah not a bad idea. Depends on the situation. In some cases, I like to let it just not catch the exception and then interrupt the script, so that I can then decide how to handle it (and whether it makes sense for the script to keep running).

Sometimes I know I can safely "ignore" the whole iteration so I catch all and "skip" whatever I'm doing that for that item, and/or log it.

If nothing else, it would be a great place to put a breakpoint!

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin
Anyone know a good way to get started on desired state configuration?

I've got some basic templates, like for adding IIS, but I'm not sure how I would do other roles or features.

Edit:

Here's what I'm looking at:

http://blogs.technet.com/b/privatecloud/archive/2013/08/30/introducing-powershell-desired-state-configuration-dsc.aspx

In the example, where it says
WindowsFeature IIS
{
Ensure = "present"
Name = "Web-Server"
}

Web-Server comes from Get-WindowsFeature
Is the "IIS" part just arbitrary?

Dr. Arbitrary fucked around with this message at 16:01 on Jan 31, 2015

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Dr. Arbitrary posted:

Anyone know a good way to get started on desired state configuration?

I've got some basic templates, like for adding IIS, but I'm not sure how I would do other roles or features.

Edit:

Here's what I'm looking at:

http://blogs.technet.com/b/privatecloud/archive/2013/08/30/introducing-powershell-desired-state-configuration-dsc.aspx

In the example, where it says
WindowsFeature IIS
{
Ensure = "present"
Name = "Web-Server"
}

Web-Server comes from Get-WindowsFeature
Is the "IIS" part just arbitrary?

The "IIS" part is a name for the action. This lets you do things like:

code:
WindowsFeature NET45
{
      Ensure = "present"
      Name = "AS-NET-Framework"
}

WindowsFeature IIS
{
      Ensure = "present"
      Name = "Web-Server"
      DependsOn = "[WindowsFeature]NET45"
}

Get-WindowsFeature is going to be the easiest way to find the names of all the features.

And you'll probably want something from the DSC resource kit before too long, so here it is:
https://gallery.technet.microsoft.com/scriptcenter/DSC-Resource-Kit-All-c449312d

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin
So, is NET45 something that is explicitly defined somewhere or is that a name that you just decided would be a good description for the part in the braces?

Edit:

I found what I need:
https://technet.microsoft.com/en-us/library/dn282127.aspx

Dr. Arbitrary fucked around with this message at 16:55 on Jan 31, 2015

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Dr. Arbitrary posted:

So, is NET45 something that is explicitly defined somewhere or is that a name that you just decided would be a good description for the part in the braces?

Edit:

I found what I need:
https://technet.microsoft.com/en-us/library/dn282127.aspx
NET45 is an arbitrary name that you choose. The feature name you put in the "Name = " part is explicitly defined (as Ithaqua pointed out, Get-WindowsFeature is the best way to see those).

DSC is pretty cool. I've deployed a pull server, used secure credentials in configs, written custom resources, etc. There are definitely quirks and things that are confusing about how to use it. Are you starting with a Push configuration (where you just apply the config manually to a node)? That's a good way to start off with the declaritive syntax before adding in the complexities of a Pull server.

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin

Briantist posted:

NET45 is an arbitrary name that you choose. The feature name you put in the "Name = " part is explicitly defined (as Ithaqua pointed out, Get-WindowsFeature is the best way to see those).

DSC is pretty cool. I've deployed a pull server, used secure credentials in configs, written custom resources, etc. There are definitely quirks and things that are confusing about how to use it. Are you starting with a Push configuration (where you just apply the config manually to a node)? That's a good way to start off with the declaritive syntax before adding in the complexities of a Pull server.

I think I've got it under control now:

I made two configs, switching the Present and Absent switches:
code:
Configuration Lab2
{
      LocalConfigurationManager
      {
              RebootNodeIfNeeded = $true
      }     
      Node "ServerB"
      {
            WindowsFeature PrintSvc
            {
                  Ensure =  "Present"
                  Name = "Print-Services"
            }
            WindowsFeature InstallIIIS
            {
                  Ensure =  "Present"
                  Name = "Web-Server"
            }
            WindowsFeature GroupPolicyManagement
            {
                  Ensure =  "Present"
                  Name = "GPMC"
            }
            WindowsFeature GUI
            {
                  Ensure =  "Absent"
                  Name = "User-Interface-Infra"
            }
            WindowsFeature GUIShell
            {
                  Ensure =  "Present"
                  Name = "Server-Gui-Shell"
            }
     }
}
It actually worked!
Not sure how to move from here to a push or pull server but I think I've gotten over the initial hump.

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Dr. Arbitrary posted:

I think I've got it under control now:

I made two configs, switching the Present and Absent switches:

It actually worked!
Not sure how to move from here to a push or pull server but I think I've gotten over the initial hump.
Nice! Push mode is when you use Start-DscConfiguration to apply the config; you don't use a separate server with it. That's likely what you're doing now.

Also, you can have the LocalConfigurationManager section in the same config, but you don't have to. I mean either way, you're going to have a different config for the LCM vs the Node (when you execute this config you're going to get 2 MOF files), so you can configure the LCM in a separate file if you prefer. When put in the same file, one has no effect on the other.

ConfusedUs
Feb 24, 2004

Bees?
You want fucking bees?
Here you go!
ROLL INITIATIVE!!





I have a little project I want to put together.

I'm not very good at programming and powershell, but I think I can do this. Before I get into the nitty-gritty details, I want to kind of break it down into pieces and see if I'm missing anything obvious.

Here's the general idea:

I have an application that runs jobs on a schedule. Every job can have one of three statuses: success, failure, or warning. Failures and warnings always log some kind of error code as to what went wrong. For example, if access was denied, you might get ACCESS_DENIED. All of these are logged in a database.

I want to run a script that will trawl through jobs since <date>, pull out the Failures and Warnings, dump the details to a html file, and provide links to knowledgebase articles about each of these states so that users can self-troubleshoot.

(Or so our less competent support guys can at least start on the right foot)


I already know how to pull the info from the database. We do this already for other stuff, and I can just crib from that. I also have a list of which KB links correspond to which codes.

So I can pull the info and dump it to a file. That's not hard.

This is where I get a bit fuzzy. Should I dump it to a CSV file, compare with my list, and add a "links" column; once that's done, I could output to a HTML table or something.

Or should I do something else? Anyone have general advice?

myron cope
Apr 21, 2009

Here's a probably dumb question.

I have a script that creates text files. In this example, there are 3 files. I want to merge them into 1 file, but only if the same text is in all 3 files. If it's not in all 3, it can just be discarded.

I'm doing
code:
Get-Content "file-*.txt" | Sort-Object -Unique
But now that I'm actually looking at it, it's just combining all 3 of them and then keeping only 1 instance of the text--not searching to see if the same text is in all 3. (It's working as it should, I just didn't realize what it was doing at first)

The text is mostly random IP addresses.

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams

Briantist posted:

What about something like this?

I took out -AsJob.

You're already returning an object which is good, and it already has an error property, even better.

As you pointed out, you can't throw an exception on the remote side and catch it on the local side. That's because it's a completely different session or runspace. Any objects sent back are serialized and so are no longer "live".

So I changed the code a bit so that when there is no error, it just sets the error property in your object to $null. When there is an exception, just use $_ and don't bother with the internal exception's message (right now we just want to pass this back to the caller).

Then back on the calling side, you check for an error in the property. At this point, you could JUST use this method (an if statement) instead of try/catch, but I'm assuming you want to use try/catch so if the error exists, then we just throw the error as is (which you can then catch and handle as necessary).

I haven't tested this yet, but it's fairly workable and much easier than using jobs.

Jobs give the added benefit of asynchronous-ness but your code as is could easily fail if the remote job were not yet completed. So you'd need more code to check on the status of the jobs that are waiting and receive the results when necessary.

So I put this aside for a few days, and came back to it on Monday. The reason I was using -AsJob was that I was trying to catch the case where invoke-command couldn't connect to the machine for whatever reason. But after letting it sit for a while and coming back, I decided to add "-ErrorAction Stop" to the Invoke-Command so now it stops and properly throws an error when it can't connect.

ElGroucho
Nov 1, 2005

We already - What about sticking our middle fingers up... That was insane
Fun Shoe

myron cope posted:

Here's a probably dumb question.

I have a script that creates text files. In this example, there are 3 files. I want to merge them into 1 file, but only if the same text is in all 3 files. If it's not in all 3, it can just be discarded.

I'm doing
code:
Get-Content "file-*.txt" | Sort-Object -Unique
But now that I'm actually looking at it, it's just combining all 3 of them and then keeping only 1 instance of the text--not searching to see if the same text is in all 3. (It's working as it should, I just didn't realize what it was doing at first)

The text is mostly random IP addresses.

I wish I could say "Do it in Python" without sounding like an rear end in a top hat

myron cope
Apr 21, 2009

ElGroucho posted:

I wish I could say "Do it in Python" without sounding like an rear end in a top hat

Yeah I'm sure Powershell is not the greatest for this. But I know a little of Powershell and none of Python. Maybe I'll look into python though anyway.

ElGroucho
Nov 1, 2005

We already - What about sticking our middle fingers up... That was insane
Fun Shoe
Should work in python 2 & 3, hope it gives you a start

code:
def make_dict(fin):
    new_dict = dict() # Dictionaries search crazy fast
    for line in fin:
        ip = line.rstrip() # strips new line character
        new_dict[ip] = 0 # Creates key ip; Value doesn't matter
    return new_dict
    
def main():
    fin1 = open('file1.txt', 'r')
    fin2 = open('file2.txt', 'r')
    fin3 = open('file3.txt', 'r')
    
    file1 = make_dict(fin1)
    file2 = make_dict(fin2)
    file3 = make_dict(fin3)
    
    outfile = open('output.txt', 'w')
    for ip in file1:
        if (ip in file2) and (ip in file3):
            outfile.write(ip + '\n')
            
    fin1.close()
    fin2.close()
    fin3.close()
    outfile.close()
main()

Adbot
ADBOT LOVES YOU

myron cope
Apr 21, 2009

I think python (or something that isn't powershell, at least) is ultimately a better option, but I need to, you know, learn some python first.

So in the mean time, I've settled on this horrid workaround:

code:
Compare-Object (gc "file1.txt") (gc "file2.txt") -PassThru -IncludeEqual | Where-Object { $_.SideIndicator -eq '==' } | Sort-Object 
Which seems to work. It would seem -ExcludeDifferent would be what I want but it never had any output (that I could pipe at least) and I can't figure out why.

If it's not abundantly clear, I basically hacked this together without really knowing more than the basics of powershell just from searching online

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