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
The Fool
Oct 16, 2003


Try -like or -contains

E: also, I like to wrap comparisons in ()’s, but it shouldn’t be necessary

Ex: ((x -eq y) -and (z -like w))

The Fool fucked around with this message at 23:04 on May 1, 2018

Adbot
ADBOT LOVES YOU

The Fool
Oct 16, 2003


If you're language agnostic about it, Python + Pandas may be a better choice.

If you still want to use powershell, check out this blog post: http://ramblingcookiemonster.github.io/Join-Object/

The Fool
Oct 16, 2003


PSA: Be aware of typing.

The Fool
Oct 16, 2003


Noted and fixed.

In this script the floats(now decimals) are just used in a couple conditions. The actual data is all strings coming from one csv to another.

The Fool
Oct 16, 2003


I'm literally just merging two CSV's, the conditions in my screenshot are from the VSCode debugger and are watching some code that I wrote to do some sanity checks. (making sure the files selected have the expected data, follows the spec from the vendor, the data isn't obviously bad, etc)

Maybe I'll think twice about posting some quirk that amuses me next time.

The Fool
Oct 16, 2003


Anyone have any suggestions on handling XML templating?

I need to build xml requests to interact with an API and don't really feel like
code:
$request = $start + $userid + $middle + $phoneNumber + $end
is the best way to handle it.

The Fool
Oct 16, 2003


Sanity check.

I am using VSTS to automate a bunch of stuff. I have some scripts that require having credentials to service accounts. I have this as a preliminary solution, but want to make sure I'm not being totally terrible.

I use this function to generate an encrypted string and key:
code:
function New-EnvCredentials {
    # Get credentials to save
    $sourceCredentials = Get-Credential -Message "Enter credentials you wish to save."

    # generate key
    $key = New-Object Byte[] 32
    [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($key)

    # generate encrypted string using key
    $password = $sourceCredentials.Password | ConvertFrom-SecureString -Key $key

    # write credentials to screen
    Clear-Host
    Write-Host "Username: $($sourceCredentials.UserName)"
    Write-Host "Password:"
    Write-Host "---"
    Write-Host $password
    Write-Host "---"
    Write-Host "Key: $key"
    Write-Host "---"
    Read-Host -Prompt "Press Enter to clear screen after saving password and key"
    Clear-Host
}
Then I take the username, password, and key and save them as environment variables in VSTS.
Then I have them available as environment variables in the script that needs the credentials, and can use this function to build a credential object.

code:
function Get-EnvCredentials($username, $password, $key) {
    $securePassword = $password | ConvertTo-SecureString -Key $key
    $credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $securePassword
    return $credentials
}

The Fool
Oct 16, 2003


New Yorp New Yorp posted:

Why can't you just store them as encrypted values in the build/release definition and pass them into the script when running?

Because VSTS doesn't pass encrypted variables as enviroment variables, I would have to pass them as command line arguments which means they would end up getting logged in plain text.

peak debt posted:

You can store a password in DPAPI like this:

DPAPI is encrypted per account and per machine. The entire point is to be able to store credentials in a way that doesn't require me pregenerating and storing them for a dozen servers.

The Fool
Oct 16, 2003


Your VSTS repo needs to be the remote origin for your local repo, then you commit and push and your changes will show up.

The Fool
Oct 16, 2003


Powershell comparisons use argument flags.

Your comparison should be
code:
if ($i -eq 30) {
    read-host "PRESS ENTER TO CONTINUE..."
}

The Fool
Oct 16, 2003


code:
$string = "*string containing wildcards*.xlsm"
$workbooks = Get-ChildItem -Path "\\my\file\path\goeshere" -Include $string -Recurse

foreach ($workbook in $workbooks)
{ 
    Invoke-Item $workbook
    start-sleep -seconds 5

    if (($workbooks.IndexOf($workbook) % 30) -eq 0) {
        Read-Host "Pausing after 30 workbooks!"
    }
}
Best of both worlds?

The Fool
Oct 16, 2003


Wicaeed posted:

Has anyone run into issues with VSCode not properly honoring PSBreakpoints while debugging Powershell?

I'm used to ISE, in that I add a breakpoint & ISE always honors it, however in VSC I add a breakpoint on something even as simple as a comment and VSC just blows right past it sometimes.

The heck?

IIRC, VSCode breakpoints only trigger on executed code. As a result, you cannot place breakpoints on empty lines or in comments and expect them to trigger.

Also, I have no idea how this compares to ISE, but if you put a breakpoint on a line in VSCode, it will trigger before the line is executed.

The Fool
Oct 16, 2003


nielsm posted:

Does anyone know of a PS module or method for reading/querying/updating SharePoint lists? The idea being to write tools in PS that fetch some data and store in a SharePoint list for a web-based lookup tool to use.

Use the CSOM

https://social.technet.microsoft.com/wiki/contents/articles/29518.csom-sharepoint-powershell-reference-and-example-codes.aspx

The Fool
Oct 16, 2003


my cat is norris posted:

Hi, friends. I'm looking for some help with Azure AD. I'm trying to mass-remove Yammer licenses from everyone in my environment, but I'm having a dickens of a time putting together a functional PowerShell script. Most guides I've found online refers to the MSOLService commands that have been replaced with AzureAD commands (I think?). A straight update of scripts doesn't seem to work for me, though.

Here's what I've learned so far:

I can get a single AzureAD account with Get-AzureADUser.

I can get a list of that user's assigned licenses and plans.

I can get a list of sub-SKUs for the Enterprise Pack license, so I know the SKUs for the products I want to disable (Yammer).

I can assign a license (ENTERPRISEPACK) while disabling the desired sub-SKU for a single user.


What I can't figure out is a) how to generate a list of all users who have the ENTERPRISEPACK license enabled and b) how to apply the above Yammer removal to those licensed users only.

Any help appreciated! :shobon:

This one-liner should get you a list of everyone with ENTERPRISEPACK.

code:
# Get all azure ad members with specific skuid in AssignedLicenses property
$EnterprisePackUsers = get-azureaduser -All $true -Filter "UserType eq 'Member'" | Where-Object { $_.AssignedLicenses.SKUID -eq "6fd2c87f-b296-42f0-b197-1e91e994b900" }
From there, I would wrap your code to disable Yammer in a ForEach.
code:
# license object generation goes here

# for each objectid of $EnterprisePackUsers, set the generated license object
ForEach ($AzureADUserObjectId in ($EnterprisePackUsers.ObjectId)) {
    Set-AzureADUserLicense -ObjectId $AzureADUserObjectId -AssignedLicenses $LicensesToAssign
}

The Fool
Oct 16, 2003


There is actually a a git specific thread. It is fairly slow, but enough people have it bookmarked that questions get answered.


The Fool fucked around with this message at 23:31 on Oct 1, 2018

The Fool
Oct 16, 2003


Use try {} catch {} ?

The Fool
Oct 16, 2003


CzarChasm posted:

I'm trying to write a small powershell script that will go to active directory and return all records (users and distribution groups) and the boolean value of the attribute msExchRequireAuthToSendTo which will be either true, false, or not set.

Unfortunately something is broken. All I have so far is
code:
Get-ADUser -Filter {msExchRequireAuthToSendTo -eq $TRUE}
And that just returns me to the command prompt, returns no values.

Even if that did work, I would have to run it three times to get a version for all three values. And even then, I don't know what the argument for neither true nor false would be.

Is there some way to get just the account name or email address and the value of the attribute in a simple two column list?
Is powershell the best way to go about this or is there a better way?

I probably don't need to add that I'm new to powershell and don't have a lot of experience with it.

Get-Aduser won't return groups, you should use get-adobject.

Following Inspector_666's advice, something like this should work:
code:
 Get-ADObject -Filter * -Property msExchRequireAuthToSendTo | Select DistinguishedName, ObjectClass, msExchRequireAuthToSendTo | Where {$_.ObjectClass -eq "group" -or $_.ObjectClass -eq "user"}
Then you can export that to a csv, or do whatever else you want.

The Fool
Oct 16, 2003


I prefer ForEach in scripts.

I have used ForEach-Object in one liners, but generally if my one liners is starting to get that complicated I probably need to unpack it a bit.

The Fool
Oct 16, 2003


FISHMANPET posted:

fam just gently caress me up
code:
foreach ($i in (0..($array.Length-1))) {
    $array[$i]
}

This is how I felt the first time I tried to do a for loop in Python.

The Fool
Oct 16, 2003


I've always done loops in C/C++/C# as

code:
for (int i = 0; i <= 10; i ++) {}
Python threw me for a loopha because it was the first time I needed to do anything like this:
code:
for i in range(0, 10):
I don't have a CS degree, or do any real development though.

The Fool
Oct 16, 2003


I recently wrote up a script to migrate our on-prem distribution lists to O365 with the ultimate goal of eliminating our hybrid exchange server.

As part of the script I wanted to be able to connect to on-prem exchange and O365 at the same time, so combined a couple different methods that I found and came up with this:

code:
    Write-Host "Connecting to O365.."
    $O365Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $Credential -Authentication Basic -AllowRedirection
    Write-Host "Connecting to on-prem Exchange server.."
    $OnPremSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://onpremserver.contoso.com/PowerShell/ -Authentication Kerberos -Credential $Credential
    Write-Host "Importing commands for O365.."
    Import-Module(Import-PSSession $O365Session -DisableNameChecking -AllowClobber) -Global -Prefix O365
    Write-Host "Importing commands for on-prem Exchange server.."
    Import-Module(Import-PSSession $OnPremSession -DisableNameChecking -AllowClobber) -Global -Prefix OnPrem
Which allows me to do stuff to both O365 and on-prem exchange at will.

code:
    Write-Host "Getting info from on-prem list: $DistributionList"
    $OnPremList = Get-OnPremDistributionGroup $DistributionList
    Write-Host "Getting info from O365 list: $DistributionList"
    $O365List = Get-O365DistributionGroupMember $DistributionList
Don't forget to close your connections.
code:
    Write-Host "Closing connections.."
    Get-PSSession | ForEach-Object {Remove-PSSession $_}

The Fool fucked around with this message at 21:51 on Nov 1, 2018

The Fool
Oct 16, 2003


Instead of passing the array to Invoke-Command, wrap it in a For-Each so you know which computer it is running against, and it will run in the order specified by the csv.

code:
  # Read input file of computer names
  $computers = Get-Content C:\support\testnames.csv
  For-Each ($computer in $computers) {
    # Test for RPC reg key that will hold port config value
    $result = invoke-command -computername $computers -scriptblock {test-path -Path "HKLM:\Software\Microsoft\RPC\Internet"}
    Write-Host "$computer returned $result"
  }
e: seriously, 3 spaces? you monster

The Fool fucked around with this message at 19:34 on Nov 29, 2018

The Fool
Oct 16, 2003


Yeah, the for-each will be slower, but as you have observed, you lose control.

The Fool
Oct 16, 2003


You could do the cheap way:
code:
Write-Host "$computer, $path"
Or the right way:
code:
#at beginning of script
$output | Select-Object Computer,Result

#put this next line inside your for-each, after your invoke-command
$output.Computer = $computer
$output.Result = $path

#then, after the for-each
$output | Export-CSV "destinationpathto.csv" -NoTypeInformation

The Fool fucked around with this message at 20:03 on Nov 29, 2018

The Fool
Oct 16, 2003


Because I felt like wasting time at work:

code:
invoke-command -computername (Get-Content C:\support\testnames.csv) -scriptblock {test-path -Path "HKLM:\Software\Microsoft\RPC\Internet"} | Select-Object @{ Name="Computer"; Expression = {$_.PSComputerName}}, @{ Name = "Result"; Expression = {$_}}

nielsm posted:

In fact, I'm pretty sure PS remoting automatically decorates returned objects with note properties containing the host that produced it.


It does, it's the PSComputerName member, I had to look it up

The Fool
Oct 16, 2003


MJP posted:

I'm sorry, but I'm totally confused by where you want me to put these. I don't know if this is what you meant.

I wasn't very clear, and was wrong anyway. I will fix it in this space.

edit:

If you have a thing returning results like your script, usually what you want to do is to build a custom object that you can easily export to a CSV.

There are a number of different ways to create custom objects in Powershell, but I like using Select-Object for simple objects since I feel it is easier to read.

For Example, this creates a custom object with the properties "Computer" and "Result"
code:
$result | Select-Object Computer,Result
Used in a script, it would look something like this:
code:
$results = @() #this creates an empty array

For-Each ($computer in $computers) {
  $result = invoke-command -computername $computer -scriptblock {test-path -Path "HKLM:\Software\Microsoft\RPC\Internet"}
  $result | Select-Object Computer,Result # creates our custom object with the properties you want
  $result.Computer = $computer # sets the computer name
  $result.Result = $result # sets the result

  $results += $result  #append custom object to array
}

$results | Export-CSV "destination.csv" -NoTypeInformation # write results to csv for review

The Fool fucked around with this message at 20:37 on Nov 29, 2018

The Fool
Oct 16, 2003


Yes

The Fool
Oct 16, 2003


If you want to get some practice in, Advent of Code is up and is fun.

The Fool
Oct 16, 2003


Is there a reason that ConvertTo-JSON and ConvertFrom-JSON wouldn't work for you?

The Fool
Oct 16, 2003


I wouldn't do it in Powershell, but maybe either C# or Python using something like Selenium

The Fool
Oct 16, 2003


Instead of
code:
Get-ADUser -Filter *
Use

code:
Get-AdUser -Filter {extensionAttribute7 -like "*"}
And you will have your results filtered for you.

edit; the full command:
code:
Get-ADUser -filter -Filter {extensionAttribute7 -like "*"} -SearchBase "OU=SomeOU,DC=contoso,DC=com" -properties extensionAttribute7 | Select-Object name, extensionAttribute7 | export-csv $dir$\ExAt7.csv -Encoding "Unicode" -NoTypeInformation

The Fool
Oct 16, 2003


Because I was a dummy testing you and put -filter in there twice.

The Fool
Oct 16, 2003


Removing the first -Filter works for me.

Are you changing the searchbase to match your domain and OU structure?

What is the error you are getting?

The Fool
Oct 16, 2003


I can all but guarantee that your searchbase value is wrong somehow.

The Fool
Oct 16, 2003


CzarChasm posted:

I'm a complete idiot when it comes to PS. I downloaded a module to get access to a gmail account, and I can't even get the first step to go because I don't understand the syntax.

code:
New-GmailSession [[-Credential] <PSCredential>] [<CommonParameters>]
I'm not sure how to pass it the user ID and password. I've tried to enter the values under PSCredentail, but I just can't get the items correct. Can you please help me?

PSCredential is expecting a credential object.

The easiest way to get one is to type the following before your cmdlet.


code:

$cred = Get-Credential

Then use the variable $cred as the value for the -Credential flag.

The Fool
Oct 16, 2003


PierreTheMime posted:

If you need it in an automated way, you can pass your user password into the following to generate a credential object, assuming you can access it locally:

code:
$CredentialObject = New-Object System.Management.Automation.PSCredential("USER",(ConvertTo-SecureString "PASSWORD" -AsPlainText -Force))

Depending on security concerns, you probably don't want to actually do this in a script since you will be saving your password in plain text.

The Fool
Oct 16, 2003


What happens when you double quote --workspaces, but single quote WorkspaceProperties? Is that when you get the error, or were you double quoting both of them?

The Fool fucked around with this message at 19:46 on Jan 29, 2019

The Fool
Oct 16, 2003


You can try using the call operator with string concatenation:
code:
$command = 'aws'
$arguments = 'workspaces create-workspaces --workspaces'
$settings = 'DirectoryId=blahblah,UserName=' + $user.SamAccountName + ',RootVolumeEncryptionEnabled=True,UserVolumeEncryptionEnabled=True,VolumeEncryptionKey=arn:aws:kms:stuffheretoothatdoesntmatter,WorkspaceProperties="{RunningMode=AUTO_STOP,RunningModeAutoStopTimeoutInMinutes=60,RootVolumeSizeGib=80,UserVolumeSizeGib=50}"'

& $command $arguments $settings

The Fool
Oct 16, 2003


Not sure if typo, but it should be

code:
$_.Name
not
code:
$._Name

Adbot
ADBOT LOVES YOU

The Fool
Oct 16, 2003


Irritated Goat posted:

Now that I'm at a PC, the code I'm using is:

code:
Invoke-Command -ComputerName XXXXX {gwmi Win32_PnPSignedDriver | select devicename,driverversion | Select-Object DeviceName | Select-String 'Canon'}
I get the response I want out of that.

If I do
code:
Import-CSV C:\Temp\PCs.csv | ForEach-Object {Invoke-Command -ComputerName $._Name {gwmi Win32_PnPSignedDriver | select devicename,driverversion | Select-Object DeviceName | Select-String 'Canon'}}
I get nothing in response.

Stop doubling up on Select-Object and use Where-Object instead of Select-String.

Something like
code:
Where-Object { $_.DeviceName -like "Canon*"}

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