powershell

Bulk create Yammer groups using Windows PowerShell

Hello again,

Recently I’ve been playing with Yammer. One of my customers requested a way to bulk import Yammer groups including group membership from their Active Directory. The first part was simply to export their Active Directory group membership data into CSV, something that has been covered in many technical blog posts before so we’re not going to cover it here. The other part was to import the CSV data into Yammer.

The process consists of two major steps. The first step is to login to Yammer using Windows PowerShell by creating an access token. Yammer uses REST API. There is a beautiful blog post by Dave Young that I’ve used here. The second step is the groups creation.

First, let us start by registering a Yammer to be able to generate an access token for Windows PowerShell to use.

  1. Open the Yammer Portal and Login using a verified admin account
  2. Expand the dotted menu and choose “APPS”
    Screen1
  3. Click on “My Apps” button
    Screen2
  4.  Click on “Register New App” button
    Screen3
  5. Supply the values as the example below (set the values relative to your tenant)
    Screen4
  6. Once you are done, make sure you copy the “Client ID”, “Client secret”, and “Expected redirect” values into the “Token Creator.ps1” script
    Screen5

The “Token Creator.ps1” which was inspired by Dave Young in his post here goes like this:

$clientID = "KKiVk**********A0lWA"
$clientsecret = "JTINYDBQ********************UUyz4AiNV8xg"
$RedirURL = "https://www.yammer.com/*****.com"
 
$ie = New-Object -ComObject internetexplorer.application
$ie.Visible = $true
$ie.Navigate2("https://www.yammer.com/dialog/oauth?client_id=$clientID&redirect_uri=$RedirURL")

Sleep 100

$UrlTidy = $ie.LocationURL -match 'code=(......................)';
$Authcode = $Matches[1]
$ie = New-Object -ComObject internetexplorer.application
$ie.Visible = $true
$ie.Navigate2("https://www.yammer.com/oauth2/access_token.json?client_id=$clientID&client_secret=$clientsecret&code=$Authcode")

The script will open a new IE window (requires PowerShell 3.0 or above) and navigate to a special URL that allows your Office365 user account trust the Yammer App you just created. After that, another IE window will appear that will prompt you to download your access token. Download the file and rename it to “access_token.json”.

You can then edit the script “Group Creator.ps1” with the location of the new access token, and the csv file with the following format:

Screen-Shot-2016-06-06-at-11.59.04-AM

The script will create a group with the specified name and add the semicolon separated users in the “Members” field (user email) as members in the group. In case a “GroupId” was supplied, the script will assume that the group is already created, and will attempt to append the users to the group members.

Note that you can get the group Id of any existing group by navigating to the group’s page and copying the feed Id value from the navigation bar.

Capture

The script “Group Creator.ps1” goes like this:

 $TokenPath = 'C:\Workspace\access_token.json'
$CSVPath = 'C:\Workspace\Yammer Groups.csv'

###Reading token###############################################################################################
$Openjson = $(Get-Content $TokenPath ) -join "`n" | ConvertFrom-Json
$token = $Openjson.access_token.token
###############################################################################################################

###Creating header#############################################################################################
$Headers = @{
"Accept" = "*/*"
"Authorization" = "Bearer "+$token
"accept-encoding" = "gzip"
"content-type"="application/json"
}
###############################################################################################################


###Reading groups##############################################################################################
$GroupItems = Import-Csv -Path $CSVPath

foreach ($GroupItem in $GroupItems)
{
###Creating Yammer group#######################################################################################
$Group = $GroupItem.Group

if ($GroupItem.Type -eq 'Private')
{
$GroupType = $true
}
else
{
$GroupType = $false
}

$CreateGroupUri = "https://www.yammer.com/api/v1/groups.json?name=$Group&private=$GroupType"

$CreateGroupResponse = Invoke-RestMethod -Method Post -Uri $CreateGroupUri -Header $Headers
###############################################################################################################

###Enumerating members#########################################################################################
$Members = $GroupItem.Members.Split(';')

foreach ($Member in $Members)
{
###Adding users to Yammer group################################################################################
$GroupID = $CreateGroupResponse.id
$UserEmail = $Member

$GroupMembershipUri = "https://www.yammer.com/api/v1/group_memberships.json?group_id=$GroupID&email=$UserEmail"

$GroupMembershipResponse = Invoke-RestMethod -Method Post -Uri $GroupMembershipUri -Header $Headers
###############################################################################################################
}
###############################################################################################################
}
############################################################################################################### 

The script may generate errors if a group with the same name already exists, or the user is already a member of that group. These errors can be ignored.

 

I hope you can find this useful.

Hazem Elshabini

Automating Hyper-V Cluster Creation with Windows PowerShell

As an Infrastructure Consultant, I’ve dealt a lot with the activity of creating a Hyper-V cluster. Since the release of Windows Server 2012, it has been possible to automate every task in the creation of a cluster. So i have created this script that would help me do that, and i thought I’d share it with you.

Introduction

Creating a Hyper-V Cluster comes with a lot of tips and best practices that you should follow, in order to create the best Hyper-V cluster there is. As the activity of creating the cluster itself might not be that difficult, however all these best practices take a long time to do on all the servers.

Let me shed the light of what this script covers:

  • Prepares Network Adapters on the Cluster Nodes, in terms of renaming, IP Address, Sunbet, Gateway, DNS Servers
  • Prepares the cluster nodes, by installing required roles and features
  • Creates Hyper-V cluster (no storage)
  • Renaming cluster networks and applying metrics

I know there are still a lot of things to add, so feel free to write in the comments any additions you can contribute.

#region Functions

function Prepare-NetAdapter
{
    [CmdletBinding()]
    [OutputType([String])]
    Param
    (
        # NetAdapterOldName help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [String]
        $NetAdapterOldName,

        # NetAdapterNewName help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=1)]
        [String]
        $NetAdapterNewName,

        # NetAdapterIPAddress help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=2)]
        [String]
        $NetAdapterIPAddress,

        # NetAdapterPrefixLength help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=3)]
        [int]
        $NetAdapterPrefixLength,

        # NetAdapterDefaultGateway help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=4)]
        [String]
        $NetAdapterDefaultGateway,

        # NetAdapterDNSServerAddresses help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=5)]
        [String]
        $NetAdapterDNSServerAddresses
    )

    Write-Output "Getting Network Adapter"

    $__NetAdapter = Get-NetAdapter -Name $NetAdapterOldName;

    Write-Output "Renaming Network Adapter"

    $__NetAdapter | Rename-NetAdapter -NewName $NetAdapterNewName;

    if (($NetAdapterIPAddress) -and ($NetAdapterPrefixLength) -and ($NetAdapterDefaultGateway))
    {
        Write-Output "Setting Network Adapter IP Address"

        $__NetAdapter = Get-NetAdapter -Name $NetAdapterNewName
        $__NetAdapter | Set-NetIPInterface -Dhcp Disabled
        $__NetAdapter | New-NetIPAddress -IPAddress $NetAdapterIPAddress -PrefixLength $NetAdapterPrefixLength
    }

    if ($NetAdapterDNSServerAddresses)
    {
        Write-Output "Setting Network Adapter DNS Addresses"
        Set-DnsClientServerAddress -InterfaceAlias $NetAdapterNewName -ServerAddresses $NetAdapterDNSServerAddresses
    }
}

function Prepare-ClusterNode
{
    Write-Output "Adding Windows Features"

    Add-WindowsFeature Multipath-IO
    Add-WindowsFeature Hyper-V -IncludeManagementTools
    Add-WindowsFeature Failover-Clustering -IncludeManagementTools
    Add-WindowsFeature RSAT-Clustering –IncludeAllSubFeature

    Write-Output "Restarting Server"

    Restart-Computer -Force
}

function Create-HyperVCluster
{
    [CmdletBinding()]
    [OutputType([String])]
    Param
    (
        # ClusterName help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [String]
        $ClusterName,

        # ClusterNodes help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=1)]
        [String[]]
        $ClusterNodes,

        # ClusterIPAddress help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=2)]
        [String]
        $ClusterIPAddress,

        # HeartbeatNetworkSubnet help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=3)]
        [String]
        $HeartbeatNetworkSubnet,

        # HeartbeatNetworkName help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=4)]
        [String]
        $HeartbeatNetworkName,

        # CSVNetworkSubnet help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=5)]
        [String]
        $CSVNetworkSubnet,

        # CSVNetworkName help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=6)]
        [String]
        $CSVNetworkName,

        # LiveMigrationNetworkSubnet help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=7)]
        [String]
        $LiveMigrationNetworkSubnet,

        # LiveMigrationNetworkName help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=8)]
        [String]
        $LiveMigrationNetworkName,

        # ManagementNetworkSubnet help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=9)]
        [String]
        $ManagementNetworkSubnet,

        # ManagementNetworkName help description
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=10)]
        [String]
        $ManagementNetworkName,

        # DiskOnly help description
        [Parameter(Mandatory=$false,
                   PositionalBinding=$false)]
        [String]
        $DiskOnly,

        # NodeAndDiskMajority help description
        [Parameter(Mandatory=$false,
                   PositionalBinding=$false)]
        [String]
        $NodeAndDiskMajority,

        # NodeAndFileShareMajority help description
        [Parameter(Mandatory=$false,
                   PositionalBinding=$false)]
        [String]
        $NodeAndFileShareMajority,

        # ClusterQuorum help description
        [Parameter(Mandatory=$false,
                   PositionalBinding=$false)]
        [SwitchParameter]
        $NodeMajority
    )
    Write-Output "Importing Fail-over Cluster module"

    Import-Module FailoverClusters;

    Write-Output "Creating new cluster"

    New-Cluster -Name $ClusterName -Node $ClusterNodes–StaticAddress $ClusterIPAddress -NoStorage

    Write-Output "Waiting 10 seconds for the cluster to initialize"

    Start-Sleep -s 10

    Write-Output "Configuring quorum"

    if ($DiskOnly)
    {
        Set-ClusterQuorum -DiskOnly $DiskOnly
    }
    elseif ($NodeAndDiskMajority)
    {
        Set-ClusterQuorum -NodeAndDiskMajority $NodeAndDiskMajority
    }
    elseif ($NodeAndFileShareMajority)
    {
        Set-ClusterQuorum -NodeAndFileShareMajority $NodeAndFileShareMajority
    }
    else
    {
        Set-ClusterQuorum -NodeMajority
    }

    Write-Output "Renaming the cluster networks"

    (Get-ClusterNetwork | where-object {$_.Address -eq $HeartbeatNetworkSubnet}).Name = $HeartbeatNetworkName
    (Get-ClusterNetwork | where-object {$_.Address -eq $CSVNetworkSubnet}).Name = $CSVNetworkName
    (Get-ClusterNetwork | where-object {$_.Address -eq $LiveMigrationNetworkSubnet}).Name = $LiveMigrationNetworkName
    (Get-ClusterNetwork | where-object {$_.Address -eq $ManagementNetworkSubnet}).Name = $ManagementNetworkName

    Write-Output "Setting metric for CSV cluster network"

    (Get-ClusterNetwork $CSVNetworkName).Metric=900;
}

#endregion

$Credential = Get-Credential
$ClusterNodes = Import-Csv ClusterNodes.csv
$NetAdapters = Import-Csv NetAdapters.csv
$Servers = ($ClusterNodes | Select-Object Name | Foreach-Object { $_.Name })

foreach ($Node in $ClusterNodes)
{
    $NodeNetAdapters = $NetAdapters | Where-Object { $_.Node -eq $Node.Name }

    foreach ($NetAdapter in $NodeNetAdapters)
    {
        Invoke-Command -ComputerName $Node.Name -Credential $Credential -ScriptBlock ${function:Prepare-NetAdapter} -ArgumentList $NetAdapter.NetAdapterOldName,$NetAdapter.NetAdapterNewName,$NetAdapter.NetAdapterIPAddress,$NetAdapter.NetAdapterPrefixLength
    }
}

Invoke-Command -ComputerName $Servers -Credential $Credential -ScriptBlock ${function:Prepare-ClusterNode}

Start-Sleep -Seconds 360

Invoke-Command -ComputerName $Servers -Credential $Credential -ScriptBlock ${function:Create-HyperVCluster} -ArgumentList "Servers-Cluster",$Servers,"10.40.30.230","222.222.222.0","Heartbeat","222.222.223.0","CSV","222.222.224.0","LiveMigration","10.40.30.0","Management"

How to use

  • This script must run from another machine, other that the cluster nodes
  • Don’t forget to Set-ExecutionPolicy RemoteSigned on this machine and the cluster nodes
  • Copy the attached files (Create-HyperVCluster.ps1, ClusterNodes.csv, NetAdapters.csv) to C:\Scripts\
  • Modify the two csv files as per your environment
  • Modify the last line of the script Create-HyperVCluster.ps1 as per your environment

And you’re good to go, let me know how it works out.