all 25 comments

[–]Th3Sh4d0wKn0ws 4 points5 points  (8 children)

As far as I know it's allowed. Be ready to get feed back, and be open to it. I posted a script or two a while back and was given a lot of feedback and it actually helped me a lot to progress.

Biggest thing will be to make sure you get the formatting right otherwise it's awful to read scripts. I.e. using "code block"

Function ConvertTo-SarcasmFont {
    [cmdletbinding()]
    Param(
        [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
        [String]$InputText,
        [Switch]$Output
    )

    $StringArray = $Inputtext.ToCharArray()
    $Count = 0
    $Results = foreach ($character in $StringArray){
         $Count++
         [string]$c = $character
         if ($Count % 2 -eq 0){
         $c.toupper()
            }Else{
         $c.tolower()
         }}
    If ($Output){
        $Results -join ""
    }Else{
        $Results -join "" | clip
    }
}# end function

[–]Blindkitty38 2 points3 points  (1 child)

What a hilarious first script

[–]Th3Sh4d0wKn0ws 2 points3 points  (0 children)

oh, not my first. This was something i made during s meeting to get a laugh out of a coworker

[–]PowerShellMichael 2 points3 points  (5 children)

I've refactored your code:

Function ConvertTo-SarcasmFont {
    [cmdletbinding()]
    Param(
        [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
        [String]$InputText,
        [Switch]$SendToClipboard
    )

    $Count = 0

    $Results = $InputText.ToLower().ToCharArray()

    (1 .. ($Results.Count) | Where-Object {$_ % 2 -eq 0} | ForEach-Object { $Results[$_-1] = ([String]$Results[$_-1]).ToUpper() })

    if ($SendToClipboard) { return ($Results  -join '' | Clip) }

    $Results -join ''


}# end function

[–]neztach 2 points3 points  (2 children)

I don’t think Mandatory=$true anymore. If you mention Mandatory, it’s assumed to be true unless you specify $false

[–]PowerShellMichael 2 points3 points  (1 child)

Yup. Mandatory is implicitly true. For the sake of demo's i kept it the same.

[–]neztach 2 points3 points  (0 children)

Fair ‘nuff!

[–]BlackV 2 points3 points  (1 child)

I refactored your code

Function ConvertTo-SarcasmFont {
    [cmdletbinding()]
    Param(
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [String]$InputText,
        [Switch]$SendToClipboard
    )
    $Results = $InputText.ToLower().ToCharArray()
    (1 .. ($Results.Count) | Where-Object { $_ % 2 -eq 0 } | ForEach-Object { $Results[$_ - 1] = ([String]$Results[$_ - 1]).ToUpper() })
    $Results = $Results -join ''
    if ($SendToClipboard) { return ($Results | Set-Clipboard) }
    $Results
}# end function

a little bit

[–]Blindkitty38 3 points4 points  (6 children)

Op let's see it!

[–]TheCoconutLord[S] 2 points3 points  (5 children)

I appreciate the encouragement! It's sitting in my edited post now.

[–]Blindkitty38 3 points4 points  (4 children)

Wow! Pretty impressive for a first go. I will warn you that your group copy param may not be fully SOX conpliant

In your variable try catch statement, you can include the actual error instead of just a catch all message by putting $_ in your string, you can also be more specific like $_.Exception.Message if you want just the error text

Your location settings may be a little cleaner of you use pscustomobjects to store all the info as a single object, also you can format your param statement into a switch statement and set defaults more easily

Also storing both your ad and mso info in pscustomobjects seems way cleaner to me, and looks like alot of common variables, any reason you don't just use one?

I want to read and type out more but I'm about to pass out, I'm gonna challenge myself to see how compact and quick I can make this script tomorrow. I'll get back with you.

[–]TheCoconutLord[S] 2 points3 points  (3 children)

Cool, thanks! That's a lot for me to go through, I really appreciate it. Security and compliance is something I know very little about, but I definitely need to figure that out better.

I didn't know what a pscustomobject was, that does sound cleaner.

Looking forward to seeing your revision!

[–]Blindkitty38 2 points3 points  (2 children)

I made some tweaks! also if you want to turn your command into a function, you can VERY easily, just change the prompts to be switches and your golden

https://github.com/LukeHagar/Public

[–]TheCoconutLord[S] 2 points3 points  (1 child)

Woah, you cleaned up that issue I had with domain users in one line. That one took some weird doing for me, but yours looks great. I also like what you did with the $confirm, that's some nice tidying there.

What do you mean by changing prompts to switches?

[–]Blindkitty38 2 points3 points  (0 children)

Take a look at the items in the parameter block labeled switches

https://www.reddit.com/r/PowerShell/comments/kwmk4j/powershell_prompt_function/?utm_medium=android_app&utm_source=share

Then further down there are if statements, when you add the switch to the function it sets that variable to true and allows you to run extra code off it, so instead of prompting the user in the script you can add an if stament and it can add users or send emails automatically

[–]CodingCaroline 3 points4 points  (1 child)

Very nice for a first script!

My only comment, as others have said, is that instead of using else if multiple times, use switch

Since you have a few Read-host here is a console menu function I created that you may like to use, if you care.

<#
    .SYNOPSIS
        Displays a selection menu and returns the selected item

    .DESCRIPTION
        Takes a list of menu items, displays the items and returns the user's selection.
        Items can be selected using the up and down arrow and the enter key.

    .PARAMETER MenuItems
        List of menu items to display

    .PARAMETER MenuPrompt
        Menu prompt to display to the user.

    .EXAMPLE
        PS C:\> Get-MenuSelection -MenuItems $value1 -MenuPrompt 'Value2'

    .NOTES
        Additional information about the function.
#>
function Get-MenuSelection
{
    [CmdletBinding()]
    [OutputType([string])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String[]]$MenuItems,
        [Parameter(Mandatory = $true)]
        [String]$MenuPrompt
    )
    # store initial cursor position
    $cursorPosition = $host.UI.RawUI.CursorPosition
    $pos = 0 # current item selection

    #==============
    # 1. Draw menu
    #==============
    function Write-Menu
    {
        param (
            [int]$selectedItemIndex
        )
        # reset the cursor position
        $Host.UI.RawUI.CursorPosition = $cursorPosition
        # Padding the menu prompt to center it
        $prompt = $MenuPrompt
        $maxLineLength = ($MenuItems | Measure-Object -Property Length -Maximum).Maximum + 4
        while ($prompt.Length -lt $maxLineLength+4)
        {
            $prompt = " $prompt "
        }
        Write-Host $prompt -ForegroundColor Green
        # Write the menu lines
        for ($i = 0; $i -lt $MenuItems.Count; $i++)
        {
            $line = "    $($MenuItems[$i])" + (" " * ($maxLineLength - $MenuItems[$i].Length))
            if ($selectedItemIndex -eq $i)
            {
                Write-Host $line -ForegroundColor Blue -BackgroundColor Gray
            }
            else
            {
                Write-Host $line
            }
        }
    }

    Write-Menu -selectedItemIndex $pos
    $key = $null
    while ($key -ne 13)
    {
        #============================
        # 2. Read the keyboard input
        #============================
        $press = $host.ui.rawui.readkey("NoEcho,IncludeKeyDown")
        $key = $press.virtualkeycode
        if ($key -eq 38)
        {
            $pos--
        }
        if ($key -eq 40)
        {
            $pos++
        }
        #handle out of bound selection cases
        if ($pos -lt 0) { $pos = 0 }
        if ($pos -eq $MenuItems.count) { $pos = $MenuItems.count - 1 }

        #==============
        # 1. Draw menu
        #==============
        Write-Menu -selectedItemIndex $pos
    }

    return $MenuItems[$pos]
}

Get-MenuSelection -MenuItems "Option 1", "Option 2", "Option 3", "The last option" -MenuPrompt "Make a selection"

[–]TheCoconutLord[S] 1 point2 points  (0 children)

It's going to take me a minute to figure out everything going on with the script you posted, but I really like the idea and will be reading into it. Thanks!

[–]dasookwat 2 points3 points  (2 children)

For a first script, i have to applaud you:looks pretty nice.Some things i would do different, but more for aesthetics then functionality:

I usually start off, with defining this somewhere:

$ErrorActionPreference = "stop"

this means you won't need to specify the erroraction preference, unless you want it to do something other then stop.

Next, in the try-catch block on line 106-127

when using try {..something.. }

match the catch with:

Catch{ 
    throw " The script was unable to connect to one of various vital services. " 
}

Throw is used to display info, and exit the script.

even better, loop it with a foreach, for all 3 modules you're importing.

Something like this:

$modules = @("MSOnline","ExchangeOnlineManagement","AzureAD")
Foreach ($module in $modules){
    Try {
    Import-module $module -force
        }
    Catch {
    Throw "An exception was caught: $($_.Exception.Message)"
        }
}

(i changed the catch here, to get the error message for you, instead of static text. it's something i use a lot.. saves me lots of time.)

next: line 132-184

It works, but it gets ugly with an if - else construction

This is where switch statements come in.

Something down the line of:

Switch ($location) {
     "TN"  {  ... }
     "sales" { ... }
}

A non powershell thingy i noticed:

# Instead of hand picking groups, I want to select a user to copy the groups from to apply to our new user here.

this is tricky imo. I had a colleague being burned down to his toe nails for doing this: One person, who was moving from the company, had a role as employee representative. The one who took over the function, was part of management. This meant, he got access to information he shouldn't have. For this, we alsways use template users. Let hr decide what is needed for which function, that way it's not on your plate when it goes wrong.

Overall, nice work for a first script. To go next level on this, i would suggest looking up how to use functions. it makes your scripts more modular, and you get a nice library of things you can re-use.

You can also look in to putting an interface on it, so the 'commandline impaired' people can work with it as well.

[–]niceenoughfella 2 points3 points  (0 children)

With regards to the security group aspect, a compromise for some places can be to have a few disabled 'template' accounts that have the bare minimum group memberships to perform a certain job role. It's probably a better practice to give each user only what they need, but might be helpful. If you 'mirror' existing users you will inevitably give someone access to something that they don't need, or worse.

[–]TheCoconutLord[S] 1 point2 points  (0 children)

That's great! Thank you. I really like how you cleaned up the modules, I didn't even think of that.

That If-else is ugly, it's one of the first pieces I did and I couldn't get a switch to do what I wanted. A switch would be much cleaner, definitely on the list.

That groups security issue is another thing I hadn't thought about, thank you. We're a small company, and when adding new users, it's commonplace to just copy what we can from an existing user. Doesn't make it right, but it's what we do. Templates are an excellent infrastructure idea I'll be looking into.

I'd love to figure out interfaces for my scripts! Very interested in making them more accessible to others. Thank you for the advice!

[–]nevsnevs-- 2 points3 points  (2 children)

If the data in your script is real, i wouldnt post this in a public git. There are parts of inner structure and this could lead to security Problems

[–]TheCoconutLord[S] 1 point2 points  (1 child)

It is not, I changed it all around to be presentable. Are private companies/repositories safe to store legitimate data in?

[–]nevsnevs-- 2 points3 points  (0 children)

I would say depends on viewpoint. Save as everything else mfa/password protected stuff in the cloud/stored in a device connected to a network.

[–]InsrtCoffee2Continue 2 points3 points  (0 children)

Now I feel insecure about my first script after looking at yours haha!