Pablo's Powershell Pow-Wow

Discussion in 'Business & Enterprise Computing' started by PabloEscobar, Sep 18, 2014.

  1. Ravennoir

    Ravennoir Member

    Joined:
    May 1, 2007
    Messages:
    6,397
    Location:
    Melbourne
    Ive checked it again, and Im not convinced thats when it is pausing

    It looks like it has now run once, I am going to monitor to see if its still able to run

    whats the easiest\best way to monitor whats happening in a schedule task script
     
  2. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    12,929
  3. Ravennoir

    Ravennoir Member

    Joined:
    May 1, 2007
    Messages:
    6,397
    Location:
    Melbourne
    Oh yeah, I forgot about Start-Transcript

    Will have to remember to use it more
     
  4. freaky_beeky

    freaky_beeky Member

    Joined:
    Dec 2, 2004
    Messages:
    1,123
    Location:
    Brisbane
    Good Day PowerShell people,

    I have an issue I cannot understand, and am hoping someone else can tell me what's up.

    I have some error handling configured in the following script, and everything works as expected, however I receive the following line in my transcript:
    Code:
    PS>TerminatingError(Get-ADUser): "Cannot find an object with identity: '<user>' under: '<Domain_DN>'."
    
    I cannot understand why, even though the error is "caught", this error is still written to the transcript, yet not in the console (when it should not be in either).

    Code:
    #requires -Module ActiveDirectory
    #requires -version 3.0
    
    #Verbose forced on for example only
    $VerbosePreference = 'continue' 
    
    $logPath = "$env:TEMP\TestAD.log"
    Start-Transcript -Path $logPath -Force
    
    $userAccountName = 'AUserThatDefinitelyDoesNotExist!'
    $createUser = $false
    
    #Check if user exists, if not set create to true
    try
    {
        Write-Verbose -Message "Checking if user account, '$userAccountName' exists"
        $userResult = Get-ADUser -Identity $userAccountName -ErrorAction Stop
        Write-Verbose -Message 'User Found'
    }
    catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
    {
        Write-Verbose -Message 'Account does not exist, will attempt creation'
        $createUser = $true
    }
    catch
    {
        Write-Warning -Message "Unhanlded exception during account lookup, with error, $($Error[0].Exception.Message)"
    }
    
    Stop-Transcript -ErrorAction SilentlyContinue
    Start-Process -FilePath $logPath -ErrorAction SilentlyContinue
    
    Any ideas/suggestions?
     
  5. pduthie_au

    pduthie_au Member

    Joined:
    May 6, 2004
    Messages:
    1,853
    Location:
    Ballarat
    I've used this approach in my user sync script and it appears to work

    $adusercheck = get-aduser -ldapfilter "(samAccountName=$usercheck)"

    if ($adusercheck -eq $Null)
     
  6. freaky_beeky

    freaky_beeky Member

    Joined:
    Dec 2, 2004
    Messages:
    1,123
    Location:
    Brisbane
    Thanks for your input.

    The script works as expected, it is merely the inconsistent (or unexpected behaviour) experienced by the Transcript.

    It appears this issue is prevalent in PowerShell v6 as well, according to this issue.
     
  7. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    24,408
    it seems like such a simple task but i've come up against a problem.

    the goal is to get a list of AD users who have a mobile phone.
    so...

    Code:
    get-aduser -searchbase $Searchbase -filter * -properties Name, Mobile | where {$_.Mobile -ne $null} | select name,mobile | export-csv $file
    very simple right?

    except i kept getting usernames returned who did not have a mobile.
    i would get a result like
    User1 040xxxxxxx
    User2 040xxxxxxx
    User3
    User4 040xxxxxxxx
    User5

    etc.

    It turns out for some fucking reason some of the accounts have a blank space in the mobile field rather than a null value.

    so my question, can i easily tell it to ignore blank spaces?
     
  8. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    12,929
    where {$_.Mobile -like "0*"}

    /lazy
     
  9. freaky_beeky

    freaky_beeky Member

    Joined:
    Dec 2, 2004
    Messages:
    1,123
    Location:
    Brisbane
    I much prefer the simpler

    Code:
    where {$_.Mobile}
    
    or the full command
    Code:
    get-aduser -searchbase $Searchbase -filter * -properties Name, Mobile | where {$_.Mobile} | select name,mobile | export-csv $file
    
    edit:
    Or perhaps you want to validate that they are 7 or 8 digit numbers with a little quick regex
    Code:
    get-aduser -filter * -properties Name, Mobile | where {($_.Mobile -replace '\s*','') -match '\d{7,8}'} | select name,mobile
    
     
    Last edited: Aug 17, 2018
  10. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    24,408
    that still picks up entries containing only blank space.

    this of course did the trick.
    it never occured to me to use -like.
    i don't know why i was only thinking of a value being either there or not there. because i'm an idiot probably.

    thanks.
     
  11. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    12,929
    However, someone has used E.123 notation in their number, and its +614xxxxxxxx, so they get missed.
     
  12. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    24,408
    in my case, we don't do that so it's fine.

    but yeah, something to keep in mind.
     
  13. Embercide

    Embercide Member

    Joined:
    Jun 17, 2002
    Messages:
    1,819
    Location:
    Brisbane
    I'm trying to create a (simple) calculator script - read products from a CSV, apply a markup to the cost price and output a new CSV containing the marked up prices. And another column showing "profit".
    I thought this would be a simple task but clearly i'm lacking some fundamental knowledge. Here is my script so far:
    Code:
    $sourceCSV = "X:\datafeed.csv"
    $destinationCSV = "X:\Profit_Calculator.csv"
    
    $importedCSV = Import-Csv -Encoding UTF8 -Path $sourceCSV | select 'item number', name, price, rrp
    $importedCSV | Add-Member markedup -NotePropertyName MarkedUp
    $importedCSV | Add-Member profit -NotePropertyName Profit
    
    foreach ($product in $importedCSV) {
        #add 58% markup
        $product.markedup = [int]($importedCSV.price * 1.58)
    
        #show difference as profit
        $product.profit = [int]($product.markedup - $importedCSV.price) 
    }
    
    $importedCSV | Export-CSV $destinationCSV -encoding UTF8
    The error i'm getting is
    I've done my best on trying to google how to change the member type to system.int32 but I can't seem to find anything dumbed down enough for me to translate into my use case.

    Any help appreciated!
     
  14. Grant

    Grant Member

    Joined:
    Jan 23, 2002
    Messages:
    1,258
    Location:
    Wollongong
    From the docs it looks like the object representing the CSV only stores strings. The default value (I'm guessing) will be an empty string, which might be what's causing your specific error. Do you need to re-cast back to string? As in:
    Code:
    $product.markedup = ([int]($importedCSV.price * 1.58)).ToString()
    (don't trust me on the parentheses, I'm on a Linux machine at the moment)
     
  15. pduthie_au

    pduthie_au Member

    Joined:
    May 6, 2004
    Messages:
    1,853
    Location:
    Ballarat
    At a guess, don't force it to an int - force it to a float - int's can't contain decimal values.
     
  16. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    12,929
    At first glance, I picked this up as well, but reading through it, there are other issues that may need to be resolved.


    ---
    Code:
    $importedCSV = Import-Csv -Encoding UTF8 -Path $sourceCSV | select 'item number', name, price, rrp
    $importedCSV | Add-Member markedup -NotePropertyName MarkedUp
    $importedCSV | Add-Member profit -NotePropertyName Profit
    
    foreach ($product in $importedCSV) {
       #add 58% markup
       $product.markedup = [int]($importedCSV.price * 1.58)
    
       #show difference as profit
       $product.profit = [int]($product.markedup - $importedCSV.price)
    }
    

    $importedCSV is an collection of objects, the objects are the things that have properties that you need to edit, not the collection, It may be wrong on my part, but If I'm adding properties to objects, and objects to collections, I tend to build the objects within the loop, and add them to a new collection at the end, then output the new collection, to wherever is needed.





    Code:
    foreach ($product in $importedCSV)
    This takes each object out of $importedCSV, and makes it available to you one at a time as $product.property.

    which should raise a red flag, when you have this line.

    Code:
     $product.markedup = [int]($importedCSV.price * 1.58)
    $importedCSV.price doesn't exist... because $importedCSV doesn't have the price property (because $importedCSV is the collection. $product is the object)


    Have a look through this and see what you think.

    Code:
    $sourceCSV = "C:\crap\datafeed.csv"
    $destinationCSV = "C:\crap\Profit_Calculator.csv"
    $importedCSV = Import-Csv -Encoding UTF8 -Path $sourceCSV | select 'item number', name, price, rrp
    
    #Create a new Array to store our objects in.
    $NewList = @()
    
    
    foreach ($product in $importedCSV) {
    
      #add 58% markup
        $markedup = ([float]$product.price * 1.58)
    
      #add value to Product Object
        $product | add-member -name "MarkedUp" -MemberType NoteProperty -Value $markedup
    
      #show difference as profit
        $profit = ([float]$markedup - $product.price)
    
        
      #Add Profit to Product Object
        $product | add-member -name "Profit" -membertype NoteProperty -Value $profit
    
      #Add Our new Product Object, to our $NewList collection
        $NewList += $Product
    }
    
    
    #Export our NewList collection to CSV
    $Newlist | Export-CSV $destinationCSV -encoding UTF8 -NoTypeInformation
    Here's my datafeed.csv

    Code:
    item number,name,price,rrp
    1,dogs,10.35,15
    2,cats,15.20,20
     
  17. Embercide

    Embercide Member

    Joined:
    Jun 17, 2002
    Messages:
    1,819
    Location:
    Brisbane
    Thanks guys, this is why I come to this thread for advice!
    My imagination pictured the foreach loop as an array of entries I could just do stuff with, but this advice is great
    Cheers
     
  18. Ravennoir

    Ravennoir Member

    Joined:
    May 1, 2007
    Messages:
    6,397
    Location:
    Melbourne
    Hey Everyone,

    So I am in the process of writing a Automation script to create User Accounts for work. We have found alot of issues have occured because of human error, so I hoping this will resolve it.

    The first step is to ensure the UserID is correctly generated based on the rules we have and ensuring its not already taken.

    For this I came up with the below script :

    Code:
    <#
    Determining Users User ID from the below rules in order
    
    First.Last
    First.MiddleInitial.Last
    FirstInitial.Last
    First.LastNumber
    
    If The UserID is over 20 Characters, the below will be used
    FirstInitial.Last
    FirstInitial.LastNumber
    #>
    
    $num = (00)
    
    #Test for if UserID First.Last exists
    Try
        {
        Get-ADUser ($First+"."+$Last)
    
        #If First.Last exists, Test for First.MiddleInitial.Last
        Try
            {
            Get-ADUser ($First+"."+$Middle.ToCharArray()[0]+"."+$Last)
    
            #If First.MiddleInitial.Last exists, Test for FirstInitial.Last
            Try
                {
                Get-ADUser ($First.ToCharArray()[0]+"."+$Last)
    
                #If FirstInitial.Last exists, Test for each number until one is available
                do
                    {
                    $num++
                    Try
                        {
                        (Get-ADUser ($First+"."+$Last+$num.ToString("00")))
                        $err = ""
                        }
                    Catch
                        {
                        $err = "error"
                        $UserID = ($First+"."+$Last+$num.ToString("00"))
                        }
                    }
                until ($err -eq "error")
    
                }
            
            #FirstInitial.Last does not exist, this will be used
            Catch
                {
                $UserID = ($First.ToCharArray()[0]+"."+$Last)
                }
            }
    
        #First.MiddleInitial.Last does not exist, this will be used
        Catch
            {
            $UserID = ($First+"."+$Middle.ToCharArray()[0]+"."+$Last)
            }
        }
    
    #First.Last does not exist, this will be used
    Catch
        {
        $UserID = ($First+"."+$Last)
        }
    
    #Ensure chosen UserID is not over 20 Characters (pre-Windows 2000 limit)
    Finally
        {
        #If the UserID is over 20 Characters, FirstInitial.Last will be used
        If($UserID.Length -ge "20")
            {
            #Test to see if FirstInitial.Last exists
            Try
                {
                Get-ADUser ($First.ToCharArray()[0]+"."+$Last)
    
                #If FirstInitial.Last exists, Test for each number until one is available
                do
                    {
                    $num++
                    Try
                        {
                        (Get-ADUser ($First.ToCharArray()[0]+"."+$Last+$num.ToString("00")))
                        $err = ""
                        }
                    Catch
                        {
                        $err = "error"
                        $UserID = ($First.ToCharArray()[0]+"."+$Last+$num.ToString("00"))
                        }
                    }
                until ($err -eq "error")
    
                }
            Catch
                {
                $UserID = ($First.ToCharArray()[0]+"."+$Last)
                }
            }   
        }
    This works well, however.....

    I thought it might be better to create a function for this, so the main script doesnt end up being massive, and in the future different parts can be reused easily.

    When I created the same script as a function (as below), it doesnt seem to work

    Code:
    <#
    .Synopsis
    
       UserID Generator
    
    .DESCRIPTION
    
       Determines Users User ID from the below rules in order
    
        FirstLast
        First.MiddleInitial.Last
        FirstInitial.Last
        First.LastNumber
        
        If The UserID is over 20 Characters, the below will be used
        FirstInitial.Last
        FirstInitial.LastNumber
    
    .EXAMPLE
      
       Generate-UserID -First "Homer" -Middle "Jay" -Last "Simpson"
    
    .INPUTS
      
       Firstname, Middlename and Surname
    
    .OUTPUTS
      
       UserID
    
    #>
    
    function Generate-UserID
    {
        #[OutputType([String])]
        Param
        (
            #Firstname
            [Parameter(Mandatory=$true)]
            [string]$First,
    
            #Middlename
            [string]$Middle,
    
            #Lastname
            [Parameter(Mandatory=$true)]
            [string]$Last
    
        )
    
    
    #number suffix       
    $num = (00)
    
    #Test for if UserID First.Last exists
    Try
        {
        Get-ADUser ($First+"."+$Last)
    
        #If First.Last exists, Test for First.MiddleInitial.Last
        Try
            {
            Get-ADUser ($First+"."+$Middle.ToCharArray()[0]+"."+$Last)
    
            #If First.MiddleInitial.Last exists, Test for FirstInitial.Last
            Try
                {
                Get-ADUser ($First.ToCharArray()[0]+"."+$Last)
    
                #If FirstInitial.Last exists, Test for each number until one is available
                do
                    {
                    $num++
                    Try
                        {
                        (Get-ADUser ($First+"."+$Last+$num.ToString("00")))
                        $err = ""
                        }
                    Catch
                        {
                        $err = "error"
                        $UserID = ($First+"."+$Last+$num.ToString("00"))
                        }
                    }
                until ($err -eq "error")
    
                }
            
            #FirstInitial.Last does not exist, this will be used
            Catch
                {
                $UserID = ($First.ToCharArray()[0]+"."+$Last)
                }
            }
    
        #First.MiddleInitial.Last does not exist, this will be used
        Catch
            {
            $UserID = ($First+"."+$Middle.ToCharArray()[0]+"."+$Last)
            }
        }
    
    #First.Last does not exist, this will be used
    Catch
        {
        $UserID = ($First+"."+$Last)
        }
    
    #Ensure chosen UserID is not over 20 Characters (pre-Windows 2000 limit)
    Finally
        {
        #If the UserID is over 20 Characters, FirstInitial.Last will be used
        If($UserID.Length -ge "20")
            {
            #Test to see if FirstInitial.Last exists
            Try
                {
                Get-ADUser ($First.ToCharArray()[0]+"."+$Last)
    
                #If FirstInitial.Last exists, Test for each number until one is available
                do
                    {
                    $num++
                    Try
                        {
                        (Get-ADUser ($First.ToCharArray()[0]+"."+$Last+$num.ToString("00")))
                        $err = ""
                        }
                    Catch
                        {
                        $err = "error"
                        $UserID = ($First.ToCharArray()[0]+"."+$Last+$num.ToString("00"))
                        }
                    }
                until ($err -eq "error")
    
                }
            Catch
                {
                $UserID = ($First.ToCharArray()[0]+"."+$Last)
                }
            }   
        }
    }
    
    Return $UserID
    Any idea why this is working ?
     
  19. Dre_

    Dre_ Member

    Joined:
    May 25, 2014
    Messages:
    823
    I got a quick issue with the difference in output from PowerShell ISE and Azure Automation.

    Running in PowerShell ISE, VM Sub ID gives the Subscription ID GUID.
    Same script in Azure Automation, VM Sub ID gives a null value.

    What am I doing wrong?

    Code:
    #Parameters
    param(
    [Parameter(Mandatory)]
    [String] $Credential,
    
    [Parameter(Mandatory)]
    [String] $TenantId
    )
    
    #Credentials
    
    $myCredential = Get-AutomationPSCredential -Name $Credential
    Login-AzureRmAccount -Credential $myCredential
    
    #Context
    $Subscriptions = Get-AzureRmSubscription -TenantId $TenantId
    
    #Loop
    foreach ( $Subscription in $Subscriptions ) {
        $SubscriptionId = $Subscription.SubscriptionId
        (Login-AzureRmAccount -Credential $myCredential -subscriptionid $SubscriptionId)>0
        (Select-AzureRmSubscription -TenantId $TenantId -SubscriptionId $SubscriptionId)>0
     
    $VMs = Get-AzureRmVM
    $vmOutput = $VMs | ForEach-Object {
        [PSCustomObject]@{
            "VM Sub Id" = $Subscription.Id
            "VM Id" = $_.Id
            "VM Name" = $_.Name
            "VM Profile" = $_.HardwareProfile.VmSize
            "VM Resource Group" = $_.ResourceGroupName
            "VM Region" = $_.Location
            "VM Availability Set" = $_.AvailabilitySetReference
            "Fault Domain" = $_.PlatformFaultDomain
            "Update Domain" = $_.PlatformUpdateDomain
            "Provisioning State" = $_.ProvisioningState
            "VMAgent Status" = $_.VMAgent.Statuses
            "VMAgent Version" = $_.VMAgent.VmAgentVersion
            "Storage Profile" = $_.StorageProfile.OsDisk.ManagedDisk.Id
            "OS Type" = $_.StorageProfile.osDisk.OsType.Value
            "OS Publisher" = $_.StorageProfile.ImageReference.Publisher
            "OS Offer" = $_.StorageProfile.ImageReference.Offer
            "OS Sku" = $_.StorageProfile.ImageReference.Sku
            "OS Version" = $_.StorageProfile.ImageReference.Version
            "VHD Location " = $_.StorageProfile.OsDisk.Vhd.Uri
            "VM OS Disk Size" = $_.StorageProfile.OsDisk.DiskSizeGB
            "VM Data Disk Size" = ($_.StorageProfile.DataDisks.DiskSizeGB) -join '-'
            "Plan Name" = $_.Plan.Name
            "Plan Product" = $_.Plan.Product
            "Plan Promo" = $_.Plan.PromotionCode
            "Plan Publisher" = $_.Plan.Publisher
            "Zones" = $_.Zones
            "NIC Count" = $_.NetworkProfile.NetworkInterfaces.Count
            "NIC Path" = $_.NetworkProfile.NetworkInterfaces
            "Extensions" = $_.Extensions
        }
    }
    $vmOutput|ConvertTo-Json -Depth 4
    
    Invoke-WebRequest `
    -Uri "https://LogicApp" `
    -Method POST `
    -Body ($vmOutput|ConvertTo-Json -Depth 4) `
    -ContentType "application/json"
    }
    
    Azure Automation
    [​IMG]

    PowerShell ISE
    [​IMG]
     
    Last edited: Jan 2, 2019
  20. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    24,408
    Story time.

    I have 36 Security groups that we use for mapping network drives.

    The path that the drive maps to is set by an attribute in the SG.
    I wanted to put that path into the Notes field of the SG to make it easier to know where it maps to (i can't use the description field for reasons).

    i have a script that exported some properties of those groups into a csv file.
    I exported CanonicalName (to confirm that all groups were in the same OU), CN, Homepage (this contains the URL).

    I deleted the CanonicalName column and deleted all group rows except for one.

    This left me a with a csv file containing the name of a single security group and the URL.

    My intention was to run a test run to update the single group listed in the csv before doing a bulk change.
    The single group left in the CSV is newly created and used only by me at the moment so no big deal if i fuck it up.

    I'm not smart enough to come up with the command myself (even something as simple as this) so i googled that shit and came up with something that looked like it would do what i wanted after a slight modification.

    Code:
    $groups = import-csv "path to csv"
    
    foreach ($group in $groups) {set-adgroup $group.samaccountname -add @{info=$group.homepage}}
    now my csv had the wrong heading for the group name so of course the command failed.
    however what i ignored about the error is that it errored 36 times. despite there only being a single group name in the csv.

    that should have raised my eyebrows somewhat.

    never the less I plowed on and changed the command from "set-adgroup $group.samaccountname" to "set-adgroup $group.CN" and ran it.

    This time I got a different error which really caught my attention.

    the gist of it was an invalid operation for a group that was not listed in the csv file.
    now i happen to know that this particular group does not actually have a homepage attribute set so straight away i had an idea what was happening.

    I checked the other security groups and noticed some of them now had the URL path listed in the notes field while some where still blank.
    I waited a bit longer and ran the script to export the properties of the groups, this time i added the Info attribute.

    Low and behold all of the security groups now had the notes field populated with the URL for the drive path (except for that one which errored at the last run because it doesn't have the homepage attribute set).


    The moral of the story is to remember to save your csv files when you change them before running commands that use them. :)
     
    Last edited: Jan 4, 2019

Share This Page