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


I made some modifications to this code that allows a powershell script to trigger a uac prompt. Specifically, I've modified it so it can run as a function, and can run from a mapped network drive.

Call this function in a script that needs admin privileges, it will trigger a uac prompt, and the re-launch the script with the credentials you entered in the UAC prompt.

I don't know how useful this is to other people, but I'm getting some mileage out of it.

code:
function Prompt-UAC
{
  # Get the ID and security principal of the current user account
  $myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
  $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)

  # Get the security principal for the Administrator role
  $adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator

  # Check to see if we are currently running "as Administrator"
  if ($myWindowsPrincipal.IsInRole($adminRole))
    {
      Write-Host Elevated Powershell
    }
    else
    {
      # We are not running "as Administrator" - so relaunch as administrator

      # Create a new process object that starts PowerShell
      $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";

      # Specify the current script path and name as a parameter
      $scriptPath = $myInvocation.PSCommandPath;
      $driveLetter = Split-Path -qualifier $scriptPath;
      If (Is-Network($driveLetter)) {
        $logicalDisk = Gwmi Win32_LogicalDisk -filter "DriveType = 4 AND DeviceID = '$driveLetter'"
        $scriptPath = $scriptPath.Replace($driveLetter, $logicalDisk.ProviderName)
      }
      $newProcess.Arguments = $scriptPath;

      # Indicate that the process should be elevated
      $newProcess.Verb = "runas";

      # Start the new process
      [System.Diagnostics.Process]::Start($newProcess);

      # Exit from the current, unelevated, process
      exit

    }
}

Adbot
ADBOT LOVES YOU

The Fool
Oct 16, 2003


MC Fruit Stripe posted:

I didn't even know Win+X was a thing - I'm more excited about learning that than this change!

It can also be accessed by right clicking on the start button.

The Fool
Oct 16, 2003


I've kinda felt that you shouldn't be doing anything with powershell that would require a GUI anyway. Your scripts should be written to be able to be ran headless, and if you need a GUI launcher type thing, do that in a different language.

The Fool
Oct 16, 2003


I have a powershell script that has been giving me a headache today.

The script queries a subsidiaries ad for user information, then updates the users account in our ad. If the account doesn't exist, it creates it.

This script works flawlessly if the account already exists, however, if the script has to create a new account, it does so, but all of the subsequent "Set-ADUser" commands fail. If I run the script a second time, it updates everything. Hell, if I even tell the function to run twice in a row, it spits out a bunch of errors on the first pass, then works fine on the second pass.

I have no idea what the gently caress to do at this point.

The Fool
Oct 16, 2003


oh god sanitizing variable names

This function is called from Main. If I call it twice, I don't get errors.

code:
function Get-UserInfo($ID)
{
  Try
  {
    $ADUser = Get-ADUser -Identity $ID -Credential $cred -Server $dc -Properties $userPropertyList
  }
  Catch
  {
    $errorText = "User " + $ID + " doesn't exist in CAC AD. Creating user now."
    Write-Error $errorText
    Create-NewUser $ID
    $ADUser = Get-ADUser -Identity $ID -Credential $cred -Server $dc -Properties $userPropertyList
  }
  return $ADUser
}
This function creates the new user.

code:
function Create-NewUser($ID)
{
  $UPN = $ID + "@contoso.com"
  New-ADUser -Name $ID -AccountPassword $defaultPassword -UserPrincipalName $UPN -ChangePasswordAtLogon $true -Credential $cred -Enabled $true
}
The Main function
code:
function Main([string]$UserID)
{
    $SubUserInfo = Get-SubUserInfo $UserID | Select-Object $userPropertyList
    $MyUserInfo = Get-UserInfo $UserID | Select-Object $userPropertyList
    $MyUserInfo = Get-UserInfo $UserID | Select-Object $userPropertyList # If I run this twice on a new user, the Set-ADUser commands work the second time.

    if (!$SubUserInfo.Enabled -or !$MyUserInfo.Enabled) {
     Write-Error "User account is disabled."
     Exit 1
    }

    Set-MyADUserProperties $SubUserInfo $MyUserInfo
    $updatedUser = Get-UserInfo $UserID | Select-Object $userPropertyList
    Write-Output $updatedUser
}

The Fool
Oct 16, 2003


Walked posted:

Gut shot from my phone, what happens if you sleep for 10 sec before calling the problematic function?

Tried Sleeps up to 30 seconds and they didn't help.

The Fool
Oct 16, 2003


So, when I was sanitizing the variable names I noticed a Write-Output that should have been a Write-Error. I fixed that in the source script, and now it's working correctly.

Thanks virtual rubber ducks.

The Fool
Oct 16, 2003


Ugato posted:

Alright I'm a bit stumped with my current project. I'm trying to automate a lot of the daily repetitive tasks my team and I face. I've actually done so pretty successfully so far but now I'm looking to automate things related to spreadsheet data.

Basically I'm starting from square 1 there. I've messed around a little but never stumbled across a way to do either of what I'm looking to do:

I have a spreadsheet. The first column is a list of 25-30 $number s which indicate a location. Based on these $number s I already have a test-connection based script which gets ping results for a bunch of formulaic IP addresses. The first step is assuredly the easiest - having one button for: read each $number, dump them all in an array and feed that array, one by one, through my function.

It may be significantly harder but I'd also like to automatically put the results in static fields on each row according to column 1's number.

Another thing that would help significantly would be done at the same time: take each $number and - in a separate spreadsheet - find the row where column 1 matches $number and pull columns (10-15) from that row. Each field is stored as a different variable to create a partial config that often times needs to be added to a router or switch.

I really only need help with the bolded sections, specifically. I can sterilize the script pretty quickly of anyone wants to look at what I have so far, but I think it wouldn't be especially enlightening or relevant. Any help would be appreciated.

I don't have any specific solutions for you, but my general approach would be to use the Excel module here to import your spreadsheet into powershell objects, do whatever processing I need, then export back to excel.

The Fool
Oct 16, 2003


I have a list of users pulled from one AD Server using this code:
code:
$userList = get-aduser -filter * -searchBase $baseOU -server $ADServer -properties $userPropertyList;
$userList = $userList | where {$_.Enabled -eq "True"} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false };
$userlist = $userlist | Where { $_.PasswordLastSet.AddDays(180) -lt ((Get-Date).AddDays(14)) };
$userList = $userlist | select EmployeeID, Name, EmailAddress, PasswordLastSet, @{Name="PasswordDateExpires";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}};
The next section is supposed to get their email addresses from a different AD server.

code:
 ForEach ($user in $userList)
 {
   $user.EmailAddress = (get-aduser -Identity $user.EmployeeID).EmailAddress;
 }
This section fails with:
code:
get-aduser : Cannot find an object with identity: '16538' under:  'dc=domain,dc=com'
But if I do a "Get-ADUser -Identity 16538" on the command line, it works fine.

Typing issue? If so how do I fix it? If not, what is going on?

Edit: More frustration, the following works fine.
code:
$user = Get-ADuser -Identity 16538 -Properties EmployeeID
Get-AdUser -Identity  $user.EmployeeID
edit2: If you're wondering, EmployeeID and sAMAccountName are always the same in my environment. The same code replacing EmployeeID with sAMAccountName errors in the same way.

edit3: it is a typing issue. The following produced the desired results.
code:
$user.EmailAddress = (get-aduser -Identity ($user.EmployeeID -as [Int32]) -Properties EmailAddress).EmailAddress;
Thanks rubber ducks.

The Fool fucked around with this message at 03:01 on Jan 31, 2017

The Fool
Oct 16, 2003


Walked posted:

AWS Free Tier dude.

Easy way to run a cloud VM for loving around with, without risking anyone's infrastructure.

Is free tier still only available for the first x number of months after account creation, or did they do away with that limitation?

The Fool
Oct 16, 2003


Because Reasons I wrote this:
code:
get-msoluser -all | Where {$_.isLicensed -eq "True"} | Select DisplayName,@{Label="License";Expression={$_.Licenses.AccountSKUID | Where {$_ -contains "contoso:DESKLESSPACK" -or $_ -contains "contoso:ENTERPRISEPACK" -or $_ -contains "contoso:STANDARDPACK"}}}

The Fool
Oct 16, 2003


beepsandboops posted:

A developer's asked me to write a script that takes a look at a file on our IIS servers, checks the modified date, then sends an email to the developers

I have no problems with the date and email parts of it, but I'm really scratching my head about how to connect to all of these different servers. As far as I can tell, this file isn't publicly accessible so I have to connect to each of these servers separately.

The servers are in different environments and different states (not all of them are domain-joined), so I don't know how to specify and securely store several sets of credentials for each server

How do other people approach working with multiple machines like this?

Have service accounts with identical credentials on all of the machines, store non-sensitive connection info in a csv.

Iterate over the csv, do your thing.

If you want to fully automate, you can store the credentials as a secure string.

The Fool
Oct 16, 2003


Inspector_666 posted:

When you do stuff like this, is the best way to get it into your workflow to add a Set-Alias line to your profile.ps1 file? That's what I've been doing with my scripts but it seems hack-y.

Make your own modules.

The Fool
Oct 16, 2003


thebigcow posted:

I also have zero experience with the AD cmdlets. Is the information you need available from Get-ADUser? If so you can probably pipe the Get-ADGroupMember results into either Get-ADUser directly, or into a ForEach-Object with a scriptblock that grabs what you need.

Piping works. Ex:

code:
get-adgroupmember -Identity "GroupName" | get-aduser -property lastlogontimestamp | select-object samaccountname, lastlogontimestamp
efb, with a better example

The Fool
Oct 16, 2003


Avenging_Mikon posted:

I piped to get-aduser and did the -properties * because it's for an audit and gently caress auditors (mostly not really). Hilariously it generates a 2MB xls file for the first group. If they come back annoyed, then I'll actually put effort in to get just the properties they wanted. What I really should have done is also find a way to make the command run against all 9 groups at once instead of modifying the script for each vpn group. I guess that will be my next lesson.

Create a list of the groups, then ForEach through it.

The Fool
Oct 16, 2003


Roargasm posted:

yeah my only real problem right now is this logic:

$nodeVMs | % { if ($noLiveMigrateVM -match "$_.Name") {
$canPatchHypervisor = 0
break
}

I can't find a comparison operator that will return true. Tried $noLiveMigrateVM -containts $_.Name, etc. Always seems to return false

Why is $_.Name surrounded by "'s?

edit: quite test seems to indicate that it doesn't matter, still looks weird.

The Fool fucked around with this message at 17:30 on Apr 27, 2017

The Fool
Oct 16, 2003


I'm wrong and dumb.

The Fool fucked around with this message at 00:12 on Apr 29, 2017

The Fool
Oct 16, 2003


Just going to leave this here as a monument to my stupidity. It took me entirely too long to figure this out.



edit: vvv- The actual code does stuff, those are just watch expressions in the VS Code debugger.

The Fool fucked around with this message at 23:29 on May 1, 2017

The Fool
Oct 16, 2003


Tried wrapping it in a try/catch?

The Fool
Oct 16, 2003


I'm doing some work with CSOM and needed to take a break to mention how awesome VS Code and the Powershell extension are.

Really starting to flex the VS Code debugger with this project.

The Fool
Oct 16, 2003


code:
$creds = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($userID, $password)
$context = New-Object Microsoft.SharePoint.Client.ClientContext($siteURL)
$context.credentials = $creds
$web = $context.Web

$requestList = $web.Lists.GetByTitle("Matrix Requests")

$matrixRequest = $requestList.GetItemById($requestID)
$context.load($matrixRequest)
$context.executeQuery()

$spoUser = $web.EnsureUser($newPersonEmail)
$context.load($spoUser)
$context.ExecuteQuery()

$matrixRequest["hkye"] = $spoUser
$matrixRequest["dfep"] = $newTask
$matrixRequest.Update()
$context.load($matrixRequest)
$context.executeQuery()

:psypop:

The Fool
Oct 16, 2003


anthonypants posted:

I want to run an updater on an application, but it's for Mozilla Firefox so the steps involved are incredibly convoluted and stupid and I hate it. It pushes the status of the updater process to a file, which is just a single word, like "applying" or "failed" or "succeeded". I also don't know how long it's going to take. Would something like do {Start-Sleep -s 60} until (Get-Content "\\path\to\update.status" = "succeeded") in a script do what I think it will?

((get-content "\\path\to\update.status") -eq "succeeded")

The Fool
Oct 16, 2003


anthonypants posted:

What I would like is for the script to hold up and not perform any of the post-install junk. I don't think that's going to do that.

It will if you put use the do..until as you wrote it, I just corrected your conditional.

code:
Do {Start-Sleep -s 3} until (("get-content c:\users\the fool\Documents\Scripts\test.txt") -eq "false"); Write-Host "Exiting."
Where test.txt is a single line file. The loop exits when the file is set to the single word "false"

Works as you would expect

The Fool fucked around with this message at 00:38 on Sep 23, 2017

The Fool
Oct 16, 2003


PowerShell Unplugged with Jeffrey Snover and Don Jones
https://www.youtube.com/watch?v=D15vh-ryJGk


This is a fun video.

The Fool fucked around with this message at 20:11 on Sep 29, 2017

The Fool
Oct 16, 2003


Briantist posted:

NYC PowerShell Meetup tonight. Serverless PowerShell using Azure functions, pretty cool stuff.

I am not in NY, otherwise I'd consider going.

I'm working on a project where one of the components is some logic done in Powershell, hosted as an Azure function, to manipulate a Sharepoint List via CSOM. It was really fun putting that together.

The Fool
Oct 16, 2003


https://github.com/powershell


edit: lol, anthonypants already submitted it: https://github.com/PowerShell/PowerShell/issues/5136

edit2: because I'm bored
code:
        /// <summary>
        /// This parameter,if true, will direct get-help cmdlet to
        /// navigate to a URL (stored in the command MAML file under
        /// the uri node).
        /// </summary>

The Fool fucked around with this message at 17:48 on Oct 17, 2017

The Fool
Oct 16, 2003


Try ConEmu

The Fool
Oct 16, 2003


Use backtick `

not caret ^

The Fool
Oct 16, 2003


Sefal posted:

I'm leaving the office now, But I just wanna say. You should give the book "Powershell in a month of lunches" a read.
It's a great book.

Edit: Really quickly at the top of my head. Would something like
(Get-Date).AddDays(-7)
work?

I’m phone posting so it’s not easy to post some sample code, but this is the right direction.

You need to convert your date strings into date-time objects, subtract 7 days from the current date, then you can do direct comparisons and filter using where-object.

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-date

The Iron Rose posted:

One other thing to note - I don't know poo poo about powershell :v: and I'm mostly just learning by doing, well, things like this at work. Any pointers would be greatly appreciated.

One liners are only good for e-penis competitions and copy-pasting. If you are writing scripts to be saved and reused, be as verbose as is practical so that it is easier to go back and maintain your poo poo afterwards.



The Fool fucked around with this message at 18:18 on Jan 15, 2018

The Fool
Oct 16, 2003


code:
$output = $output | Where-Object ((Get-Date ($_.'Last Found')) -gt ((Get-Date).AddDays(-7)))
This is how I would do it, but this is completely untested.

The Fool
Oct 16, 2003


Yeah, I mess up the curly braces all the loving time and did nothing to valid the snippet I posted.

The Fool
Oct 16, 2003


Eschatos posted:

Originally the requirement was to run the backup every 15 minutes, and I'm half expecting it to revert to that.

Shadow Copies can be run as often as you want, and they are the solution you should be using instead of kludging together a script that will break when you need it most.

The Fool
Oct 16, 2003


You should be able to specify the module namespace by typing ModuleName\CmdLetName

ie, "ActiveDirectory\Get-Aduser" and "Get-AdUser" are the same

The other option may be to pass session objects around.

I don't have the vmware modules to be able to verify your specific use case though.

The Fool
Oct 16, 2003


Is anyone aware of a better way to put a GUI wrapper around a powershell script? Even with poshgui.com, WinForms is a total loving nightmare.

The Fool
Oct 16, 2003


I had already started the project using https://www.poshgui.com so I just finished it up with what I had.

Thankfully I was just adding a GUI wrapper to a pre-existing script and only needed to do 1 form with a couple browse dialogs and go! button.

It was still a total nightmare, and if I find myself needing to do this in the future I am for sure either redoing the entire thing in C#, or doing a GUI launcher in C#.

The Fool
Oct 16, 2003


Double posting because I have a question about improving the performance of a different project.


We have a system that generates to separate CSV files. These CSV's are related, but contain different data. All of the data generated is tied to a vendor ID number.

In order to eliminate some workload on the finance team, I wrote a script that merges these to files.

The tricky part is that the source files can have multiple lines for the same vendor that contains different data and that data needs to be consolidated into one line for the final file.

This all works, but it takes about 5 minutes to run on two files with 2500 records.

The biggest bottleneck is where I check to see if a vendor already has data in the the array using Where-Object.

The function:
code:
function Get-VoucherLine($voucherCSV, $vendor)
{
    $line = $voucherCSV | Where-Object {$_."VENDOR" -eq $vendor};
    return $line
}
Basically, for every line in the source files, it is checking every line in the destination array to see if that vendor entry exists.

I'm wondering if there isn't more efficient way to locate the object in the array that matches $vendor.

Any ideas?

The Fool
Oct 16, 2003


cheese-cube posted:

Without seeing the entire script plus some sample data it's difficult to advise but it sounds like your issue is further up the chain. You're probably iterating through too many things, use "Select-Object -Unique" to extract your vendor IDs and then if necessary iterate through them. Again, it's hard to advise without samples.

I have two data files, they have more fields that contain data like contact names and addresses, and some other accounting codes that don't change.

The first file is a list of vendor codes and invoice amounts:
code:
vendor,invoice
453,$40
34,$39
67,$20
453,$32
The second is a list of vendor codes and deductions:
code:
vendor,deduction
453,$15
34,$10
34,$2
89,$32
I merge these together to into an array that I export to a csv at the end of the script. The final csv looks like this:
code:
vendor,deduction,invoice
453,$15,$72
34,$12,$39
67,$0,$20
89,$32,$0
I have two for-each loops, one processes each file.

The loops both look like this:
code:
    foreach ($line in $dividendVoucherCSV)
    {
        $temp = Get-VoucherLine $finalVoucherCSV $line."VENDOR"
        if (!$temp) {
            $temp = New-VoucherLine $line;
            $finalVoucherCSV += $temp;
            $temp = Get-VoucherLine $finalVoucherCSV $line."VENDOR"
        }
        $temp = Update-Voucher $temp $line;
I already posted the the Get-VoucherLine function.

New-VoucherLine creates a custom object with the all of the fields, and sets the fields that don't change.
Update-VoucherLine adds the deduction and invoice values to the existing line.

e:
Right after posting that I eliminated my second get-voucherline and that seemed to help a little bit, but not a huge amount.
code:
    foreach ($line in $dividendVoucherCSV)
    {
        $temp = Get-VoucherLine $finalVoucherCSV $line."VENDOR"
        if (!$temp) {
            $temp = New-VoucherLine $line;
            $temp = Update-Voucher $temp $line;
            $finalVoucherCSV += $temp;
            # $temp = Get-VoucherLine $finalVoucherCSV $line."VENDOR"
        } else {
            $temp = Update-Voucher $temp $line;
        }
}

The Fool
Oct 16, 2003


It took me more time to figure out how to export a hash table to csv than it did to convert my array to a hashtable.


I've got a 5x performance boost using a hashtable keyed on the vendor ID.

The export-csv statement looks like this now:
code:
($finalVoucherCSV.GetEnumerator() | Select-Object Value).Value | Export-CSV $destinationPath -NoTypeInformation

The Fool fucked around with this message at 19:55 on Mar 29, 2018

The Fool
Oct 16, 2003


Can you elaborate on what's strange about it?

Adbot
ADBOT LOVES YOU

The Fool
Oct 16, 2003


Not the first time I've forgotten about -ExpandProperty


cheese-cube posted:

Then from there as per my other post if you're just spitting out a 1D array of strings then it's easier to use Out-File.

It's actually a custom object with about 10 member properties.
e: each property corresponds to a column in the csv following a format defined by the financial program that is importing this file.

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