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
Venusy
Feb 21, 2007
Also, if you're planning on using the data in something else, make sure you don't use Format-Table, as the data won't be usable. For example, the output of Get-ADUser <example user> | ft | ConvertTo-CSV is:
code:
#TYPE Microsoft.PowerShell.Commands.Internal.Format.FormatStartData
"ClassId2e4f51ef21dd47e99d3c952918aff9cd","pageHeaderEntry","pageFooterEntry","autosizeInfo","shapeInfo","groupingEntry"
"033ecb2bc07a4d43b5ef94ed5a35d280",,,,"Microsoft.PowerShell.Commands.Internal.Format.TableHeaderInfo",
"9e210fe47d09416682b841769c78b8a3",,,,,
"27c87ef9bbda4f709f6b4002fa4af63c",,,,,
"4ec4f0187cb04f4cb6973460dfe252df",,,,,
"cf522b78d86c486691226b40aa69e95c",,,,,
In general, I'd avoid using Format-Table in favour of Out-GridView (ogv), providing the PowerShell ISE is installed on the machine you're running the command from. OGV gives you the data in a table, but it allows you to sort and filter it without having to export it to CSV. (It also allows you to scroll it across, while Format-Table is more limited). Or just using select to select four or less properties to look at.

Adbot
ADBOT LOVES YOU

22 Eargesplitten
Oct 10, 2010



I've been banging my head against this for a couple days. When I get to the line below, it says access denied.

code:

PSExec $computer "C:\Scripts\InstallBat.bat"

$Computer is in the format computername.domain.local. I've tried a lot of variants on the command, I don't remember all of them.

Video Nasty
Jun 17, 2003

Are you passing credentials? Have you configured WinRM and WSMan for sending commands over the network?
Also, try <DOMAIN>\<COMPUTERNAME>?

BaseballPCHiker
Jan 16, 2006

22 Eargesplitten posted:

I've been banging my head against this for a couple days. When I get to the line below, it says access denied.

code:
PSExec $computer "C:\Scripts\InstallBat.bat"
$Computer is in the format computername.domain.local. I've tried a lot of variants on the command, I don't remember all of them.

Do you have WinRM configured? Have you verified you have the rights for that PC?

nielsm
Jun 1, 2009



BaseballPCHiker posted:

Do you have WinRM configured? Have you verified you have the rights for that PC?

PSExec doesn't use WinRM.

22 Eargesplitten
Oct 10, 2010



I'm not going to have WinRM configured, do I need to? The PSExec Copy-Item on the line before works, although it also throws the same error.

I've tried passing credentials as -u $accountused.username -p $accountused.password. I've also run as the system account with -s. I also tried putting the username and password in quotes. When I have the -u and -p parameters it says unknown username or bad password. I am an administrator on the machine.

When I do domain\computer it can't find the computer. Isn't it supposed to be that format for usernames?

Video Nasty
Jun 17, 2003

22 Eargesplitten posted:

When I do domain\computer it can't find the computer. Isn't it supposed to be that format for usernames?

You are absolutely right about that, my bad. Make sure your username is formatted as domain\user.
I'm lead to agree that WinRM might not be required, but mark this important note:

ss64.com posted:

For PsExec to work, File and Printer sharing must be enabled on the remote computer.

nielsm
Jun 1, 2009



Specifically, for PSExec to work, you must have a user account that can connect to \\COMPUTER\ADMIN$ and write files there.
You also need to be able to run "sc \\COMPUTER create <parameters>" to control the service control manager on it.
PSExec uses those remote management tools to do its task.

anthonypants
May 6, 2007

by Nyc_Tattoo
Dinosaur Gum
Just got a PowerShell script from a vendor who got it from BitTitan's MigrationWiz product and literally the first line of the function is
code:
$passwordChars		= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-=!@#$%^&*()_+"
and the password generation code is:
code:
1..10 | ForEach { $adminPassword = $adminPassword + $passwordChars[$rand.next(0,$passwordChars.Length-1)] }
e: Which reminded me that a while back I wrote a PowerShell script to generate an LDAP user/password which I should probably put here, even though we got caught up doing other things and I wasn't able to test this or put it into production
code:
$ldapServer = "PUT AN IP ADDRESS HERE, INSIDE OF THE QUOTES"
$ldapPort = "THIS SHOULD BE 389 FOR LDAP OR 636 FOR SECURE LDAP, MAKE SURE IT'S INSIDE QUOTES"
$ldapHostname = "GIVE YOUR LDAP SERVER A NAME"
$ldapAdminuser = 'cn=USERNAME,dc=DOMAIN,dc=TLD'
$ldapAdminpw = 'password'
$baseDN = "ou=LDAPUSERS,dc=DOMAIN,dc=TLD"



[System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
[System.Reflection.Assembly]::LoadWithPartialName("System.Net")

$conn = New-Object System.DirectoryServices.Protocols.LdapConnection "$($ldapServer):$($ldapPort)"
$conn.SessionOptions.HostName = "$ldapHostname"
$conn.AuthType = [System.DirectoryServices.Protocols.AuthType]::Basic
$conn.SessionOptions.ProtocolVersion = "3"
$creds = New-Object "System.Net.NetworkCredential" -ArgumentList $ldapAdminuser,$ldapAdminpw
$conn.Bind($creds)
#$conn.Bind()


#Display all existing users
$filter = "(uid=*)"
$scope = [System.DirectoryServices.Protocols.SearchScope]::Subtree
$attrlist = "*"
$search = New-Object System.DirectoryServices.Protocols.SearchRequest -ArgumentList $basedn,$filter,$scope,$attrlist

$req = $conn.SendRequest($search)
#$req.Entries.Attributes['uidnumber'].getvalues('string')
$req.Entries | Select-Object @{E={$_.Attributes['uid'].GetValues('string')};Name="Username"},`
    @{E={$_.Attributes['uidnumber'].GetValues('string')};Name="User ID"},`
    @{E={$_.Attributes['gidnumber'].GetValues('string')};Name="Group ID"},`
    @{E={$_.Attributes['cn'].GetValues('string')};Name="CN"} | Sort-Object "User ID"


# Create new user
$newuser = Read-Host -Prompt "New username:"
$firstname = Read-Host -Prompt "First name:"
$surname = Read-Host -Prompt "Last name:"

$plaintext = Read-Host -Prompt "New password:" -AsSecureString
$sha = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
$rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider

if ($plaintext.Length -eq 0) {
    # create password
    $plaintext = ([char[]](33..126) | Sort-Object {Get-Random})[0..8] -join ''
    Write-Host "Temporary password is: ""$plaintext"" -- write this down!"
    $plaintext = ConvertTo-SecureString $plaintext -AsPlainText -Force

    # Welcome2016 is a very bad default password
    #$plaintext = [System.Text.Encoding]::UTF8.GetBytes("Welcome$(Get-Date -Format yyyy)")
    
    # Generate salt
    $salt = New-Object Byte[] 8
    $rng.GetBytes($salt)

    # salted SHA1 hash
    $pass_sha = $sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($plaintext)+$salt)
    # Generate base64 hash of the two
    $pass_base64 = [System.Convert]::ToBase64String($pass_sha+$salt)
} else {
    # Generate salt
    $salt = New-Object Byte[] 8
    $rng.GetBytes($salt)

    # salted SHA1 hash
    $pass_sha = $sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($plaintext)+$salt)
    # Generate base64 hash of the two
    $pass_base64 = [System.Convert]::ToBase64String($pass_sha+$salt)
}

# Verify password is good, actually
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
$sha = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider

$verifypass = [System.BitConverter]::ToString([System.Convert]::FromBase64String($pass_base64)).ToLower() -replace '-'
if ($verifypass.Length -ne 56) { Write-Host "something very bad is going on here and I do not approve" }

$verifysalt = [Microsoft.VisualBasic.Strings]::Right($verifypass,16)
$verifysalt = $verifysalt -replace '' -split "(?<=\G\w{2})(?=\w{2})" | %{ [Convert]::ToByte( $_, 16 ) }
if ($verifysalt -ne $salt) { Write-Host "how did you mess up this bad and what did you do" }

$verifysha = [Microsoft.VisualBasic.Strings]::Left($verifypass,40)
$verifysha = $verifysha -replace '' -split "(?<=\G\w{2})(?=\w{2})" | %{ [Convert]::ToByte( $_, 16 ) }
if ($verifysha -ne $pass_sha) { Write-Host "you donked up, friendo. you donked up real bad." }



# Add the user to the LDAP
$req = (New-Object "System.DirectoryServices.Protocols.AddRequest")
$req.DistinguishedName = "cn=$newuser,$baseDN"
$req.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "objectClass",@("person","inetOrgPerson")))
$req.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "givenName",$firstname))
$req.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "sn","$surname"))
$req.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "uid","$newuser"))
$req.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "uidNumber",`
    "[int]($req.Entries | Select-Object @{E={$_.Attributes['uidNumber'].GetValues('string')};Name="uid"} | Sort-Object "uid" -Descending).uid[0]+1"))
$req.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "gidNumber",`
    "[int]($req.Entries | Select-Object @{E={$_.Attributes['gidNumber'].GetValues('string')};Name="gid"} | Sort-Object "gid" -Descending).gid[0]+1"))
$req.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "homeDirectory","/home/$newuser"))                        #modify this as necessary
$req.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "userPassword","{ssha}$pass_base64"))

$doreq = $conn.SendRequest($req)
if ($req.ResultCode -ne [System.DirectoryServices.Protocols.ResultCode]::Success) {
    Write-Host "`aSomething happen! Bad!!`nResultCode: $req.ResultCode`nMessage: $req.ErrorMessage`a"
}

$conn.Dispose()

anthonypants fucked around with this message at 02:13 on Feb 16, 2018

Doug
Feb 27, 2006

This station is
non-operational.
Not sure if this has been posted before, but I found a pretty fun PoSh war game the other day. If you're familiar with the games on overthewire, this is pretty similar.

http://www.underthewire.tech/wargames.htm

22 Eargesplitten
Oct 10, 2010



Back on the PSExec credentials topic. I have everything working if I just put the username and password into the script, but if I use a get-credential and do

code:
psexec \\computer -u $accountUsed.UserName -p $accountUsed.Password -d script.bat

It gives me an error saying Logon Failure: unknown user name or bad password. I've also tried assigning the username and password to new variables in case PSexec can't read off of a PSCredential object.

I can write to the ADMIN$ folder on the remote computer. I'm having this same problem with multiple computers, but all of those computers also accept the plaintext password and username.

Zaepho
Oct 31, 2013

22 Eargesplitten posted:

Back on the PSExec credentials topic. I have everything working if I just put the username and password into the script, but if I use a get-credential and do

code:
psexec \\computer -u $accountUsed.UserName -p $accountUsed.Password -d script.bat
It gives me an error saying Logon Failure: unknown user name or bad password. I've also tried assigning the username and password to new variables in case PSexec can't read off of a PSCredential object.

I can write to the ADMIN$ folder on the remote computer. I'm having this same problem with multiple computers, but all of those computers also accept the plaintext password and username.

The password is a securestring. To get the cleartext back out you need to use
code:
$cred.GetNetworkCredential().password

22 Eargesplitten
Oct 10, 2010



Well, that gives me the password in clear text, but it still gives the same error.

Unrelated, if I were to start a script that deletes itself after completion, would that work or would it stop in it's tracks? I'm guessing it depends on whether the script is loaded all at once or one line at a time.

nielsm
Jun 1, 2009



I believe PowerShell loads the full script, then closes the script file, letting you delete the file without problems, even in the middle of execution.

DOS-derived batch files traditionally execute line by line, with the result that you could, at least under true DOS, have a batch file that modifies itself while running. I don't know how cmd.exe does things, but you should be able to delete a running batch file, just don't expect it to be able to continue running afterwards.

Pile Of Garbage
May 28, 2007



Yeah you can definitely run a PowerShell script which deletes itself. If you create a script with the following and run it then it will just delete itself without issues:

code:
Remove-Item -Path $MyInvocation.InvocationName

22 Eargesplitten
Oct 10, 2010



Yep, it works. It works so well I nearly hosed myself accidentally running it on my own computer because it deleted the git repo as well. I made a backup now, I was lucky I had a copy on a different computer.

Pile Of Garbage
May 28, 2007



For scripts that are designed to run in an unattended and non-interactive fashion what is the best way to handle logging? I've come up with a method but was wondering if anyone has anything better/can suggest improvements.

First I'll add two non-mandatory parameters to the script for specifying the log filename and path ($LogFilePath defaults to the current working directory and $LogFileName defaults to "<SCRIPT_NAME>_LogFile_<DATE>-<TIME>.csv"):

code:
Param(
    [Parameter()]
    [String]$LogFilePath = $PWD.Path,

    [Parameter()]
    [String]$LogFileName = "$($MyInvocation.MyCommand)_LogFile_$((Get-Date).ToString('yyMMdd-HHmm')).csv"
)
Then I'll create the log file and write the column headers (Sometimes I'll wrap New-Item in a try/catch block just in-case):

code:
$LogFile = New-Item -Name $LogFileName -Path $LogFilePath -ItemType File -Force
Out-File -InputObject '"Date","Time","Level","Message"' -FilePath $LogFile -Encoding UTF8 -Append
Last of all I add a script method to the object that can be used for writing an entry to the log file:

code:
Add-Member -InputObject $LogFile -MemberType ScriptMethod -Name 'WriteLogFile' -Value {
    Param(
        [Parameter(Mandatory=$true)]
        [String]$Message,

        [Parameter()]
        [ValidateSet('Info', 'Warning', 'Error', 'Critical')]
        [String]$Level = 'Info'
    )

    $Date = Get-Date
    Out-File -InputObject ("`"$($Date.ToString('yyyy-MM-dd'))`",`"$($Date.ToString('HH:mm:ss.fff'))`",`"$($Level.ToLower())`",`"$Message`"") -FilePath $this -Encoding UTF8 -Append
}
Then throughout the script I can easily write entries to the log by calling the method:

code:
$LogFile.WriteLogFile('Someone stole your trees', 'Error')
That is just for a standalone script, usually I'd have the routine in a module of common functions.

Does anyone do things differently? Is there a better way?

reL
May 20, 2007

cheese-cube posted:

For scripts that are designed to run in an unattended and non-interactive fashion what is the best way to handle logging? I've come up with a method but was wondering if anyone has anything better/can suggest improvements.

...

Depends on exactly what sort of logging I need, but I honestly usually just use start-transcript/stop-transcript. Anything that writes to the console will write to the txt file transcript, so if I want to have entries after specific events in the script one can simply Write-Host whatever they want written to the transcript file.

Pile Of Garbage
May 28, 2007



Yeah Start/Stop-Transcript is great for ad-hoc stuff or when doing debug tracing. However the majority of the stuff I write is for automation so it runs unattended, sometimes against very large sets of objects (Usually triggered by Scheduled Tasks). This means I need timestamped log entries and the ability to control exception handling so that when item 7,845 of 10,000 fails I can write to log, continue execution and then investigate the error later.

nielsm
Jun 1, 2009



If you wrap your task with a function, or even a module, and then have a small runner script that invokes it, you can use standard redirection on that.
Make sure to read Get-Help about_Redirection.

Mo_Steel
Mar 7, 2008

Let's Clock Into The Sunset Together

Fun Shoe

cheese-cube posted:

Yeah Start/Stop-Transcript is great for ad-hoc stuff or when doing debug tracing. However the majority of the stuff I write is for automation so it runs unattended, sometimes against very large sets of objects (Usually triggered by Scheduled Tasks). This means I need timestamped log entries and the ability to control exception handling so that when item 7,845 of 10,000 fails I can write to log, continue execution and then investigate the error later.

Something like this within the code (say searching a few thousand text files that should all not have a given line of text and you want exceptions whenever they do because it's an error and shouldn't be there):

code:
    # Search through each line of each document for the given string.
    foreach($doc in $documents){
        $progressNum++

        $reader = new-object System.IO.StreamReader($doc)
        while(($line = $reader.ReadLine()) -ne $null){
            # If the string is found, let the user know what document had it and stop searching this document.
            if ($line.contains($s)) { 
                 "$date -- $time : !! WARNING: Found $s in $doc, was file # $progressNum of $docNum results set !!" 
                $resultsNum++
                break
            }
        }
        $reader.Close()
    }

    ""
     "Search for '$s' completed:"
     "$docNum documents searched."
     "$resultsNum documents containing '$s' were found."
    ""
Then you can call the script and pipe it to Out-File; I've never tried piping a whole script to Out-File in scheduled tasks before, but if that's a pain you could also pipe those lines directly to a file with the append switch inside the script, but this would let you run it manually and get the output in the PS window if desired. If that's not an issue, just add something like:

code:
"$date -- $time : !! WARNING: Found $s in $doc, was file # $progressNum of $docNum results set !!"  | Out-File "C:\logs\scriptNameLog - $date.txt" -Append
etc. etc.

Mo_Steel fucked around with this message at 13:39 on Aug 24, 2016

22 Eargesplitten
Oct 10, 2010



Is there a way to tell if a computer is pulling automatic DNS addresses off the firewall or is manually configure? This is for Windows 7, all of the end cmdlets I see are only for 8.1.

anthonypants
May 6, 2007

by Nyc_Tattoo
Dinosaur Gum

22 Eargesplitten posted:

Is there a way to tell if a computer is pulling automatic DNS addresses off the firewall or is manually configure? This is for Windows 7, all of the end cmdlets I see are only for 8.1.
Are you asking how you can tell what DNS servers a Windows 7 client is using?

e: To return an object:
Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.IPEnabled} | Select-Object DNSServerSearchOrder
To return a list of the DNS servers:
Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.IPEnabled} | Select-Object -ExpandProperty DNSServerSearchOrder

The Get-WmiObject cmdlet also takes a -ComputerName parameter if you want to look up remote computers.

anthonypants fucked around with this message at 23:08 on Aug 24, 2016

22 Eargesplitten
Oct 10, 2010



Sorry, I should have been clear. My lead thinks my predecessor might have hosed poo poo up by manually configuring clients to use Google DNS instead of the ones on the DCs. He says it's fine if a computer set to automatic has a public DNS server as a fallback, but if it's manually configured it should be private. I got all of the DNS servers for all the computers I need to know about, but I can't tell if they are automatic or manual.

anthonypants
May 6, 2007

by Nyc_Tattoo
Dinosaur Gum

22 Eargesplitten posted:

Sorry, I should have been clear. My lead thinks my predecessor might have hosed poo poo up by manually configuring clients to use Google DNS instead of the ones on the DCs. He says it's fine if a computer set to automatic has a public DNS server as a fallback, but if it's manually configured it should be private. I got all of the DNS servers for all the computers I need to know about, but I can't tell if they are automatic or manual.
I guess you could compare between what the DHCP server says and what's on the client, but I don't think that's in Windows 7 PowerShell. You can, however, use netsh.

netsh interface ipv4 show config will output a list of all the interfaces' settings, and you'll either see "Statically Configured DNS Servers" or "DNS servers configured through DHCP" in the output.

vanity slug
Jul 20, 2010

I don't think you can query WMI to see whether the client is using the DNS servers from DHCP or using manual ones. It's not specified in the documentation anyhow: https://msdn.microsoft.com/en-us/library/aa394217%28v=vs.85%29.aspx

You could go old school and check it with "netsh interface ipv4 show dns".

Methanar
Sep 26, 2013

by the sex ghost

22 Eargesplitten posted:

Sorry, I should have been clear. My lead thinks my predecessor might have hosed poo poo up by manually configuring clients to use Google DNS instead of the ones on the DCs. He says it's fine if a computer set to automatic has a public DNS server as a fallback, but if it's manually configured it should be private. I got all of the DNS servers for all the computers I need to know about, but I can't tell if they are automatic or manual.

Nope. You'll have to do something like

invoke-command -computer (get-content workstations.txt) -scriptblock {

cmd /c "netsh interface ip set dns "Local Area Connection" dhcp"

}

Then make sure the dns option is set properly in dhcp. Nothing should be statically assigned except for DCs. Make dns set the primary as your DC and then do tertiary as secondary DCs and then finally 8.8.8.8 or whatever through dhcp

I can't test it because for the first time ever I don't have a windows machine but it'll probably work. If it doesn't double check netsh identifies the interface as local area connection. Apparently it might not.

Methanar fucked around with this message at 23:42 on Aug 24, 2016

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin
Is there an easy way to filter a list of objects by a second list?

Something like:

$AllVMs = Get-VM
$Targets = Get-Content c:\list.txt

$TargetVMs = $AllVMs ∩ $Targets

mystes
May 31, 2006

Dr. Arbitrary posted:

Is there an easy way to filter a list of objects by a second list?

Something like:

$AllVMs = Get-VM
$Targets = Get-Content c:\list.txt

$TargetVMs = $AllVMs ∩ $Targets
$AllVMs | ? {$Targets -contains $_}

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin

mystes posted:

$AllVMs | ? {$Targets -contains $_}

That is way better than what I was trying to write.

Jowj
Dec 25, 2010

My favourite player and idol. His battles with his wrists mirror my own battles with the constant disgust I feel towards my zerg bugs.
Anyone familiar with granting "Full Control" of an AD object to another AD object? I had asked for help with this in HangOps and Stubblyhead helped out a bit (thanks!) but after that ran into some errors that showed I needed more understanding. Backed up from doing AD stuff to just generic file permission stuff and got that ok. I pulled from a technet article and hosed around to get a better understanding. :

code:
# Pulled from here: [url]https://technet.microsoft.com/en-us/library/ff730951.aspx#mainSection[/url] and tweaked a tiny amount

$colRights = [System.Security.AccessControl.FileSystemRights]"FullControl" 
$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None 
$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None 
$objType =[System.Security.AccessControl.AccessControlType]::Allow 
$obj = get-ADComputer clusterobj01

$objUser = New-Object System.Security.Principal.SecurityIdentifier($obj.sid) 
$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `
    ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType) 

$objACL = Get-ACL "C:\Scripts\Test.ps1" 
$objACL.AddAccessRule($objACE) 

Set-ACL "C:\Scripts\Test.ps1" $objACL
Used info from that to fix some of my type declarations, but now I'm running into issues with the class I'm in. Here's the script that isn't working for setting permissions on the ADObject.
code:
# Pre-reqs set
import-module activedirectory
set-location -path "AD:\OU=SQL,OU=Servers,DC=dev,DC=test,DC=com"

# Declare FileSystemRights variables
$colRights = [System.Security.AccessControl.FileSystemRights]"FullControl" 
$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None 
$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None 
$objType =[System.Security.AccessControl.AccessControlType]::Allow 
$obj = get-ADComputer Clusterobj01
$objUser = New-Object System.Security.Principal.SecurityIdentifier($obj.sid) 
 
$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType)
 
$objACL = Get-ACL ".\cn=testsql997" 
$objACL.AddAccessRule($objACE) 

Set-ACL ".\cn=testsql997" $objACL
This results in the following error:
Cannot convert argument "rule", with value: "System.Security.AccessControl.FileSystemAccessRule", for "AddAccessRule" to type "System.DirectoryServices.ActiveDirectoryAccessRule": "Cannot
convert the "System.Security.AccessControl.FileSystemAccessRule" value of type "System.Security.AccessControl.FileSystemAccessRule" to type
"System.DirectoryServices.ActiveDirectoryAccessRule"."

I had thought that the "FullControl" was a FileSystemRight but PS is trying to convert it to a System.DirectoryServices.whatever type and is failing. But under this DirectoryServices type there isn't a "FullControl" member. So my thinking so far has found 2 possibilities:

1) You can't do this (which seems unlikely) through PS
2) FullControl for ADobjects isn't actually a FileSystem member (which makes it Very Confusing :\) and instead exists in a different class, just not the class PS is throwing as the type convert failure.

I've looked through the System.DirectoryServices Namespace and I haven't found a class that actually contains the member "FullControl', but its possible I missed it I guess. Can someone help me bridge whatever gap I've got in my understanding?

GPF
Jul 20, 2000

Kidney Buddies
Oven Wrangler

Jowj posted:

code:
# Pulled from here: [url]https://technet.microsoft.com/en-us/library/ff730951.aspx#mainSection[/url] and tweaked a tiny amount

$colRights = [System.Security.AccessControl.FileSystemRights]"FullControl" 
I've looked through the System.DirectoryServices Namespace and I haven't found a class that actually contains the member "FullControl', but its possible I missed it I guess. Can someone help me bridge whatever gap I've got in my understanding?
First thing I'd try is to change that line from what it is to this:
code:
$colRights = [System.Security.AccessControl.FileSystemRights]::FullControl
When I type it like that into my system then check Get-Member on that variable, I get this:

PS C:\Windows\System32\WindowsPowerShell\v1.0> $colRights | gm


TypeName: System.Security.AccessControl.FileSystemRights

Name MemberType Definition
---- ---------- ----------
etc

If I decided to dig into the class further, I'd probably find out that was an enum.

Pile Of Garbage
May 28, 2007



Pro-tip: always be Googling full type names, 99% of the time the first result is the relevant MSDN page: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights(v=vs.110).aspx. As GPF mentioned, that class is an enum so remove those double-quotes.

However I'd like to be a dick and question your motives: why do you need to apply full-control permissions on objects in AD and is there a reason why you can't just use inheritance? The primary reason I ask is that explicit object-level permissions rapidly become an administrative and security PITA.

Jowj
Dec 25, 2010

My favourite player and idol. His battles with his wrists mirror my own battles with the constant disgust I feel towards my zerg bugs.
Thanks GPF, cheese-cube. I do not use .net *ever* so apologies for the fundamental mistakes. I think I'm gonna buy a .net book once this quarter is over; it seems that there's a bunch of functionality in Powershell that I just can't get at well because I'm stuck not understanding .net poo poo very well.

quote:

Pro-tip: always be Googling full type names
Thanks :). If you have more Entry Tips or a place to go read them I'd definitely appreciate it.

quote:

However I'd like to be a dick and question your motives: why do you need to apply full-control permissions on objects in AD and is there a reason why you can't just use inheritance? The primary reason I ask is that explicit object-level permissions rapidly become an administrative and security PITA.

Naw, you're not being a dick, its a good question.

For context, this is part of a set of scripts I'm making for DB cluster build automation. Security doesn't want to grant the DBAs/MSSQL account permission to create instance objects in this OU so each time a new cluster is built every instance has to be manually added and have the clusterobj associated with the instance granted fullcontrol. As to why we're not doing inheritance its because I don't have enough time to get approval to change our process and then implement the change before the project is due. Everything I've read makes it look so much easier if I have poo poo configured at the OU level instead of at the individual object level, just :\.

So, no business justification really, just timelines from management.

Pile Of Garbage
May 28, 2007



Jowj posted:

Thanks GPF, cheese-cube. I do not use .net *ever* so apologies for the fundamental mistakes. I think I'm gonna buy a .net book once this quarter is over; it seems that there's a bunch of functionality in Powershell that I just can't get at well because I'm stuck not understanding .net poo poo very well.

If you're only working with PowerShell then there's very little that you have to learn specifically about .NET outside of understanding OOP fundamentals. When you understand types, classes, methods, etc. you'll be able to take advantage of pretty much any .NET class in PowerShell (Using MSDN doco of course).

Unfortunately I don't really have any recommendations regarding reading materials but others might.

Jowj posted:

Naw, you're not being a dick, its a good question.

For context, this is part of a set of scripts I'm making for DB cluster build automation. Security doesn't want to grant the DBAs/MSSQL account permission to create instance objects in this OU so each time a new cluster is built every instance has to be manually added and have the clusterobj associated with the instance granted fullcontrol. As to why we're not doing inheritance its because I don't have enough time to get approval to change our process and then implement the change before the project is due. Everything I've read makes it look so much easier if I have poo poo configured at the OU level instead of at the individual object level, just :\.

So, no business justification really, just timelines from management.

Hah, yeah I see what you're doing and I've been in that same situation. Goodluck.

GPF
Jul 20, 2000

Kidney Buddies
Oven Wrangler

cheese-cube posted:

If you're only working with PowerShell then there's very little that you have to learn specifically about .NET outside of understanding OOP fundamentals. When you understand types, classes, methods, etc. you'll be able to take advantage of pretty much any .NET class in PowerShell (Using MSDN doco of course).
Agreed. When I teach PowerShell to the guys at work, up until now, I've been staying with cmdlets since that's much more "PowerShell-y". But, these days, I've been thinking about PowerShell more like CommandLine.NET than cmdlets. The project that started it all was attempting to make a script that would hit all the print servers we have, pull queues that had jobs, and kill jobs that were older than 8 hours. As I developed it, I realized that we had a mix of 2008 R2 and 2012 R2 servers running as print servers. This was a huge deal since there are few if any printer cmdlets in 2008 R2, and the 2012 R2 cmdlets wouldn't work in a backwards compatible way against 2008 R2. WMI was as slow as Christmas, and I didn't want to just stop services, delete files, and restart services in the middle of the day. I wanted to be able to run this script at any time as many times as I wanted and not affect the users one single bit.

So, after flattening my skull against the desk and keyboard for a while, I went digging into MSDN and sample scripts that dealt with printing, print servers, queues and all that. What I found was a mixture of old tech (net view \\server), .NET types and classes regarding printing (System.Printing Namespace), and pure PowerShell to step through and deal with the returned objects, I was able to easily deal with that problem without being forced to change how I did it depending on the OS.

So, I'm writing up a new class for the other techs. Class 1 will be what I consider the foundations of PowerShell to be, and that's objects and types. Class 2 will be loops and decision making. Not sure what it'll be after that. Gotta see how they react.

Jowj, what actually helped me do better in PowerShell was learning C# in bits and pieces. That's when I started to get the idea of objects and types, and that's when I started understanding PowerShell better.

anthonypants
May 6, 2007

by Nyc_Tattoo
Dinosaur Gum

GPF posted:

Agreed. When I teach PowerShell to the guys at work, up until now, I've been staying with cmdlets since that's much more "PowerShell-y". But, these days, I've been thinking about PowerShell more like CommandLine.NET than cmdlets. The project that started it all was attempting to make a script that would hit all the print servers we have, pull queues that had jobs, and kill jobs that were older than 8 hours. As I developed it, I realized that we had a mix of 2008 R2 and 2012 R2 servers running as print servers. This was a huge deal since there are few if any printer cmdlets in 2008 R2, and the 2012 R2 cmdlets wouldn't work in a backwards compatible way against 2008 R2. WMI was as slow as Christmas, and I didn't want to just stop services, delete files, and restart services in the middle of the day. I wanted to be able to run this script at any time as many times as I wanted and not affect the users one single bit.

So, after flattening my skull against the desk and keyboard for a while, I went digging into MSDN and sample scripts that dealt with printing, print servers, queues and all that. What I found was a mixture of old tech (net view \\server), .NET types and classes regarding printing (System.Printing Namespace), and pure PowerShell to step through and deal with the returned objects, I was able to easily deal with that problem without being forced to change how I did it depending on the OS.

So, I'm writing up a new class for the other techs. Class 1 will be what I consider the foundations of PowerShell to be, and that's objects and types. Class 2 will be loops and decision making. Not sure what it'll be after that. Gotta see how they react.

Jowj, what actually helped me do better in PowerShell was learning C# in bits and pieces. That's when I started to get the idea of objects and types, and that's when I started understanding PowerShell better.
A few jobs ago when I was on the helpdesk, I wrote a PowerShell script that leveraged net use \\printserver and .Net to make a GUI interface which would allow users to install the printer closest to them. I wouldn't recommend duplicating that effort, but I learned (and forgot) a lot.

GPF
Jul 20, 2000

Kidney Buddies
Oven Wrangler

anthonypants posted:

A few jobs ago when I was on the helpdesk, I wrote a PowerShell script that leveraged net use \\printserver and .Net to make a GUI interface which would allow users to install the printer closest to them. I wouldn't recommend duplicating that effort, but I learned (and forgot) a lot.
Nice. I'm actually trying to run far away from PowerShell-backed GUIs right now. No support + ISE instability + Pain -eq $GPF.Screwed.ToString()

Here's the printer queue old job murder script (localized info removed). I essentially looked at each piece and tried to find the fastest way to do each thing. WMI was being the world's slowest dick when getting a list of queues, so I shortcut that with net view \\server. WMI is used, but only to check the server for ANY jobs. After that, it's .NET stepping through each queue. One thing that stumped me for a long time was that .NET would create an empty object when asking a PrintQueue for all its jobs. If you looked at it over time, it'd look like this: null variable, null, null, empty object, empty object, object with jobs, so just checking to see if the variable had something in it would grab an empty object even though the queue said it had jobs. That one took a while to figure out.

Looking at it now, I can see a few places where I could have been more efficient or cut 5 lines down to 2 or less, but this runs at an acceptable speed and, most importantly, does what I need it to do.

code:
#Requires -Version 3
#requires -RunAsAdministrator

$servers = [put list of servers here, quoted, separated with commas...in other words, a string array] # Server names

# create an object
[void] [System.Reflection.Assembly]::LoadWithPartialName('System.Printing')

foreach ($server in $servers) 
{
  # Check to see if printer is up and running. If not, skip.
  if (!(Test-Path -Path "\\$server\print$\x64")) 
  {
    Write-Host -Object "$server is not responding. Skipping..." -ForegroundColor Red 
    continue
  }
  $checkqueues = Get-WmiObject -Class 'Win32_PrintJob' -Namespace 'root\CIMV2' -ComputerName $server
  if (!$checkqueues) 
  {
    "No queued jobs on $server. Skipping to next server." 
    continue
  }
  # get list of shared printers
  $namelist = @()
  $list = net.exe view \\$server | Select-String -Pattern 'Print'
  foreach ($line in $list)
  { 
    $line = $line -split ' '
    $namelist += $line[0]
  }
    
  try 
  {
    $PrintServer = New-Object -TypeName system.printing.printserver -ArgumentList "\\$server" 
  }
  catch 
  {
    "Server $server is invalid or not responding." 
    continue
  }

  Write-Host -Object "$server" -ForegroundColor Green
  foreach ($name in $namelist)
  {
    try 
    {
      $PrintQueue = $PrintServer.GetPrintQueue($name) 
    }
    catch 
    {
      continue 
    }
        
    if ($PrintQueue) 
    { 
      if ($PrintQueue.NumberOfJobs -gt 0)
      {
        "   $name"
        # Change 7/29/2016
        # It is possible for this do loop to never finish. A queue may report a number > 0 because it's heavily broken.  So, I'm adding
        # a counter that will set the $myJobs array to blank and break the loop after 500 attempts. That should work. (GPF)
        $count = 0
        do 
        {
          $count++
          $myJobs = $PrintQueue.GetPrintJobInfoCollection()                      # There can be a delay with this Print queue .NET method.
          if ($count -gt 500) # 500 tries is enough. Yell, clear the variable, and break out of the loop
            { Write-Host "      QUEUE BROKEN: Reporting Jobs but will not give job info. Manually stop spooler and remove old files." -fo Red ; $myJobs = @() ; break }
        }                                                                        # Additionally, the $myJobs variable will get an empty object put into it
        until ( ($myJobs|Measure-Object).count -eq $PrintQueue.NumberOfJobs )    # that has no .count method. This do-until solves that problem 100%
        foreach ($job in $myJobs)
        {
          $id = $job.JobIdentifier
          $person = $job.Submitter
          $jname = $job.Name
          $pages = $job.NumberOfPages
          $status = $job.JobStatus
          # TimeJobSubmitted is in UTC.  Must convert to local time.
          $submitted = $job.TimeJobSubmitted.ToLocalTime()
          $now = Get-Date
          $diff = $now - $submitted
          if ($diff.TotalHours -ge 8) 
          { 
            Write-Host -Object "      Cancelling $id - $person - $jname - $pages pages - $status - $submitted" -ForegroundColor Red 
            $job.Cancel()
          } else 
          {
            Write-Host -Object "      Keeping $id - $person - $jname - $pages pages - $status - $submitted" -ForegroundColor Green 
          }
        }
      } 
    }
  }
  ''
}

22 Eargesplitten
Oct 10, 2010



I wrote a 1-liner yesterday that I had some trouble with.

code:
gci /path . -include "*.auc" /force /recurse 

I also tried without specifying the path. It worked on a few folders, but I got an error saying access was denied on most of them. I ran it as an administrator, so I don't see why that should be. I even got it on some of my own user folders.

Adbot
ADBOT LOVES YOU

mystes
May 31, 2006

22 Eargesplitten posted:

I wrote a 1-liner yesterday that I had some trouble with.

code:
gci /path . -include "*.auc" /force /recurse 

I also tried without specifying the path. It worked on a few folders, but I got an error saying access was denied on most of them. I ran it as an administrator, so I don't see why that should be. I even got it on some of my own user folders.
Why are you using slashes for parameters? Powershell uses dashes.

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