Replace NSLookup with Resolve-DnsName

Replace NSLookup with Resolve-DnsName

So we have all been there where we need to confirm that the DNS record exists, or you have a script which needs the IP address rather than the DNS address. Traditionally we would use NSLookup to validate all of this.
Recently I have needed to complete a solution based in PowerShell that I needed to create a DNS record and then validate that it exists, and that the IP address has been set correctly.
Rather than writing heaps of extra code to handle the result from NSLookup in PowerShell, or to instantiate the DNS .Net object, I spent the time to look into the PowerShell options.
This is where I stumbled upon Resolve-DnsName which is fantastic, it allows you to not only complete a generic lookup of the DNS address, but you can use the -type parameter for example -type A will bring back only the A name records for that DNS address for example:

blogpic1
If we were to then run the same query with AAAA as the type (IPv6 Address) we get the following:

blogpic2
Where the Commandlet comes into its own is that you can save it into an object and then use the result to complete your tasks which require an IP address, for example like this:

blogpic3
Obviously this is just scratching the surface of what you can do with the commandlet, but hopefully a starting point for people to think about using more PowerShell.
Good Luck
Steve

How to install Azure Stack TP2 on a server with less than 12 CPU Cores

How to install Azure Stack TP2 on a server with less than 12 CPU Cores

Currently out of the box (binaries) Azure Stack TP2 won’t install on anything less than 12 Cores on the host server be it physical or Virtual, (Yes you can install Azure Stack in Nested Virtualization to get around the Physical Disk allocation issue, but be aware there is a Blue Screen Bug when running it so not advised.)

If you’re like me and can’t get your hands on shining new kit for a lab/testing environment and need to make do with DL380 G6’s which have 8 cores and a truck load of RAM and the required number of disk which worked fast enough for Azure Stack TP1 it doesn’t work out of the box for Azure Stack TP2, so below is the process we worked out to allow for the installation of Azure Stack TP2 on under speced system.

So you have already found the config.xml file located in c:\CloudDeployment which looks something like below, and you have already done a search on the file and found the MinimumNumberOfCoresPerMachine value and updated it to the number of cores which you have, from here you reran the PowerShell command and it failed. The next step was to search the internet and find this blog.

File Edit Format View Help encoding= (Role (Publiclnfo> < Logs > <Fi1eLog Location <Fi1eLog Location <Fi1eLog Location <Fi1eLog Location "$env : SystemDrive\C10udDep10yment\Logs "*env : / > "$env : \ " / > " $env : \IPInformation . txt" / > <WindowsEventLog / > <WindowsEventLog Pattern="App1ication"/> < / Logs > Guid]</Dep10ymentGuid> </Pub1icInfo> (Privatelnfo> < ! Do not change the Deployment Guid. For re-deploying update (ExecutionContext IdempotentRun="True" < Ski pDriverInj / SkipDriverInj ection> < 3mdWaitTimeoutMinutes> / 8mdWaitTimeoutMinutes> < / 3MCWinPETimeOutInMinutes> IdempotentRun ' property to "False" < /TimeServiceStartTimeoutInMinutes> < Us eWim300t>F a Is / Us eWim800t> < / Ignore01dOSCheckResu1t> < Install Image Path " Index="" VHDName="" <LibraryShareImageF01der <VhdImageTargetDir Path < Sta / Sta rtupTimeoutInMinutes> < Shutdown / Shutdown TimeoutInMinutes> (Accounts) < / LocalAdminAccountID> <RunAsAccountID>Fabric</RunAsAccountID> < DomainAdminAccountID> Doma inAdmin< / Doma inAdminAccountID> / ChassisManagerAccountId> LocalAdmin< / BuiltInAdminAccountID> </Accounts> (ValidationRequirements> <MinimumOperati /MinimumOperatingSystemVersion> (Min PerMa ch in e) /Min imumNumberOfCores PerMa ch in e) /MinimumPhysica1MemoryPerMachineG3> <MinimumNumberOfAdaptersPerMa chin /MinimumNumberOfAdaptersPerMa chin e) /MinimumAdapterSpeed3itsPerSecond>

So now to find the real config file which is used to install the bare metal(host machine) for Azure Stack, this is located here: C:\CloudDeployment\Configuration\Roles\Infrastructure\BareMetal\OneNodeRole.xml easy right, we make the change to the MinimumNumberOfCoresPerMachine again save the file then rerun the PowerShell Command and it fails again… now this is getting annoying.

After some digging we find that during the installation of Azure Stack it creates a group of encrypted files which are located here: C:\EceStore these contain an encrypted version of the Config file from the first time you ran the PowerShell script, So we will delete this folder, and the config.xml file from C:\CloudDeployment and rerun the whole process from the start. Presto the installation process will complete this time around.

Good Luck,

Steve

Replace Telnet with Test-NetConnection PowerShell Module

Replace Telnet with Test-NetConnection PowerShell Module

While troubleshooting some issues for a client this week I needed to test that a port was open. Traditionally our first step for this would to reach for Telnet. But given it is 2016 and telnet has been a feature that has been intentionally removed by default from all Windows OS for the last 4 years as a result of the security concerns, I don’t have it enabled on my laptop.  

So, the question is if not Telnet what do I use for testing a port? Well the easy answer is: PowerShell

The CmdLet in question is “Test-NetConnection” sounds like it would just do a ping, which doesn’t really help me right? Well that’s true, but you can then use the -Port parameter which allows you to define the port which you wish to send the packet down to test that the port is open.

Here is an example of how to use the command. The first usage in the image is a failed reply, while the second usage is a successful response.

blog-image-for-test-netconnection

Good Luck.

Steve

Windows Azure Pack PowerShell SQL Resource Provider Add SQL Server Hosting Group to Plan

Windows Azure Pack PowerShell SQL Resource Provider Add SQL Server Hosting Group to Plan

This post equates to the 5th in the series of blog posts for Azure Pack, where we have gone over the process of obtaining, adding, and removing the core components of Azure Pack being the User, Plans and Subscription objects, then we moved onto the process of Adding & removing the SQL Server hosting groups, and there dependent SQL servers in the SQL Resource Provider. So in this post we will complete the circle on the administrative side by adding the newly created SQL Server group to a Plan.

Unlike the past posts where the concepts were quite self-explanatory when attaching the SQL Server hosting group to a Plan, it needs to be completed in a two-step process, the first is to associate the SQL Resource Provider to the Plan, then once it has been attached there is a requirement to allocate the actual resources by using a Quota.

From here your users will be able to create their own databases on the presented SQL Servers.

# query SQL function

function Invoke-SQL {

param([string] $connstring,[string] $sqlCommand)

$connectionString = $connstring

$connection = new-object system.data.SqlClient.SQLConnection($connectionString)

$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)

$connection.Open()

$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command

$dataset = New-Object System.Data.DataSet

$adapter.Fill($dataSet) | Out-Null

$connection.Close()

$dataSet.Tables

}

 

# get WAP connection string

$connstring = (Get-MgmtSvcSetting -Namespace adminsite | where {$_.name -eq ‘ApplicationServicesConnectionstring’}).value

$connstring = $connstring.Split(‘;’)[0] + ‘;’ + $connstring.Split(‘;’)[1] + “; Integrated Security=SSPI”

 

# get WAP websites

$adminsite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$TenantSite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘TenantSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$AuthSite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AuthSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$windowsauthsite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘WindowsAuthSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$AdminAPI = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Microsoft.Azure.Portal.Configuration.OnPremPortalConfiguration.RdfeAdminUri'” -connstring $connstring).value

$TenantAPI = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘TenantSite’ and Name = ‘Microsoft.Azure.Portal.Configuration.AppManagementConfiguration.RdfeUnifiedManagementServiceUri'” -connstring $connstring).value

$ClientRealm = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Authentication.RelyingParty'” -connstring $connstring).value.Split(‘,’)[1].replace(‘”Realm”:”‘,).replace(‘”‘,)

 

# Check is cert is signed

$admin = $adminsite.Split(‘:’)

$cert = !(New-Object System.Net.Security.SslStream((New-Object System.Net.Sockets.TcpClient($admin[1].Replace(‘/’,),$admin[2])).GetStream())).IsSigned

 

# get token

$token = Get-MgmtSvcToken -AuthenticationSite $windowsauthsite -ClientRealm $ClientRealm -Type Windows -DisableCertificateValidation:$cert

 

# Add SQL Server Group to Plan

$ResProviderName = ‘sqlservers’

$PlanName = ‘steve’

$plan = Get-MgmtSvcPlan -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -DisplayName $PlanName

$ResourceProvider = Get-MgmtSvcResourceProvider -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -Name $ResProviderName

Add-MgmtSvcPlanService -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -InstanceId $ResourceProvider.InstanceId -ServiceName $ResourceProvider.Name -PlanId $plan.ID

 

# Add SQL Quota to Plan

$ServiceName = ‘sqlservers’

$QuotaList = New-MgmtSvcQuotaList

$ResProvider = Get-MgmtSvcResourceProvider -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -Name $servicename

$SQLQuota = Add-MgmtSvcListQuota -QuotaList $QuotaList -ServiceName $ServiceName -ServiceInstanceId $ResProvider.InstanceId

$sqlquotares = Add-MgmtSvcQuotaSetting -Quota $SqlQuota -Key Editions -Value ‘[{“displayName”:”Laptop”, “groupName”:”Laptop”, “resourceCount”:”1″, “resourceSize”:”1024″, “resourceSizeLimit”:”1024″, “offerEditionId”:”081313063701″, “groupType”:null}]’

Update-MgmtSvcPlanQuota -AdminUri $AdminAPI -Token $token -QuotaList $QuotaList -PlanId $plan.Id -DisableCertificateValidation:$cert

 

# Remove SQL Server group from plan

$ResProviderName = ‘sqlservers’

$PlanName = ‘steve’

$plan = Get-MgmtSvcPlan -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -DisplayName $PlanName

remove-MgmtSvcPlanService -adminuri $adminapi -token $token -disablecertificatevalidation:$cert -ServiceName $ResProviderName -PlanId $plan.id

 

# Get list of Services attached to Plan

$PlanName = ‘steve’

$plan = Get-MgmtSvcPlan -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -DisplayName $PlanName

$plan.ServiceQuotas.settings.Value

I hope this helps and makes sense, please let me know via twitter @steve_hosko if you would like to know any more information around this.

Good Luck

Steve

Windows Azure Pack PowerShell SQL Resource Provider Add Servers & Hosting Group

Windows Azure Pack PowerShell SQL Resource Provider Add Servers & Hosting Group

Here we are again to go over some more commands for automation of Azure Pack, and in this case the addition and or removal of new/old SQL servers to your existing infrastructure, in my previous posts I have gone over the Get, Add, and Removal of Users, Plans, and Subscriptions which are the core components of Azure Pack. For this post I will go over the process of obtaining, the SQL Server Hosting Group, in this case it is a standalone group as Microsoft has not exposed the ability to add a highly available group via PowerShell.

Once we have spent our time working on the SQL Server Hosting group we will move on to adding a new SQL server to the Group, along with the removal of the SQL Server.

Much like the last posts I have included the first 33 lines where we obtain all of the required details for Azure Pack to execute the commands.

# query SQL function

function Invoke-SQL {

param([string] $connstring,[string] $sqlCommand)

$connectionString = $connstring

$connection = new-object system.data.SqlClient.SQLConnection($connectionString)

$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)

$connection.Open()

$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command

$dataset = New-Object System.Data.DataSet

$adapter.Fill($dataSet) | Out-Null

$connection.Close()

$dataSet.Tables

}

 

# get WAP connection string

$connstring = (Get-MgmtSvcSetting -Namespace adminsite | where {$_.name -eq ‘ApplicationServicesConnectionstring’}).value

$connstring = $connstring.Split(‘;’)[0] + ‘;’ + $connstring.Split(‘;’)[1] + “; Integrated Security=SSPI”

 

# get WAP websites

$adminsite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$TenantSite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘TenantSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$AuthSite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AuthSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$windowsauthsite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘WindowsAuthSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$AdminAPI = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Microsoft.Azure.Portal.Configuration.OnPremPortalConfiguration.RdfeAdminUri'” -connstring $connstring).value

$TenantAPI = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘TenantSite’ and Name = ‘Microsoft.Azure.Portal.Configuration.AppManagementConfiguration.RdfeUnifiedManagementServiceUri'” -connstring $connstring).value

$ClientRealm = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Authentication.RelyingParty'” -connstring $connstring).value.Split(‘,’)[1].replace(‘”Realm”:”‘,).replace(‘”‘,)

 

# Check is cert is signed

$admin = $adminsite.Split(‘:’)

$cert = !(New-Object System.Net.Security.SslStream((New-Object System.Net.Sockets.TcpClient($admin[1].Replace(‘/’,),$admin[2])).GetStream())).IsSigned

 

# get token

$token = Get-MgmtSvcToken -AuthenticationSite $windowsauthsite -ClientRealm $ClientRealm -Type Windows -DisableCertificateValidation:$cert

 

# Get SQL Server Hosting Group

Get-MgmtSvcSqlServerGroup -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert

 

# Remove SQL Server Hosting Group

$SQLSvrGroupName = ‘Laptop’

$SQLSvrGroup = Get-MgmtSvcSqlServerGroup -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -GroupName $SQLSvrGroupName

Remove-MgmtSvcSqlServerGroup -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -ServerGroupId $SQLSvrGroup.GroupId

 

# Add SQL Server Hosting Group

$GRPNAME = ‘Laptop’

Add-MgmtSvcSqlServerGroup -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -GroupName $GRPNAME -ResourceGovernorEnabled

 

# Get SQL Server

Get-MgmtSvcSqlHostingServer -AdminUri $adminapi -Token $token -DisableCertificateValidation:$cert

 

# Remove SQL Server

$Svrname = ‘desktop-ojt0lg6’

$sqlsvr = Get-MgmtSvcSqlHostingServer -AdminUri $adminapi -Token $token -DisableCertificateValidation:$cert | Where-Object {$_.Name -eq $Svrname}

Remove-MgmtSvcSqlHostingServer -AdminUri $adminapi -token $token -DisableCertificateValidation:$cert -HostingServerId $sqlsvr.ServerId

 

# Add SQL Server

$cred = Get-Credential -UserName ‘SA’ -Message ‘Enter the SA Password for the SQL Server’

$GRPNAME = ‘Laptop’

$SQLSize = 1024

$SQLCPU = 4

$SQLMEM = 4

$SQLIOPS = 2000

$SQLMAXResourcePools = 10

$SQLSVRName = ‘desktop-ojt0lg6’

$svrgrp = Get-MgmtSvcSqlServerGroup -AdminUri $AdminAPI -token $token -DisableCertificateValidation:$cert | Where-Object {$_.GroupName -eq $GRPNAME}

Add-MgmtSvcSqlHostingServer -adminuri $adminapi -Token $token -DisableCertificateValidation:$cert -SqlUser $cred -Name $SQLSVRName -TotalSpaceMB $SQLSize -ServerGroupId $svrgrp.GroupId -NumberOfCpuCores $SQLCPU -TotalMemoryGB $SQLMEM -SupportedIopsPerVolume $SQLIOPS -MaximumResourcePools $SQLMAXResourcePools

This is where I will leave you on this post, but stay turned for future posts around adding the SQL Server hosting group to a Plan.

Good Luck

Steve

Windows Azure Pack PowerShell sample commands – Remove Commands

Windows Azure Pack PowerShell sample commands – Remove Commands

To close out the series of the simple sample commands for Azure Pack the first being to Get the Second to Add and this one will go over the process of Removing objects from Azure Pack. Much like the first two posts the first 33 lines are the same which obtains the connection details for your Azure Pack environment, along with an authentication token.

Once we have obtained these details we will have the starting point to remove each of the objects we created in the second blog post, this needs to be completed in reverse where the Subscription is required to be removed before the Plan can be removed. Once both have been removed then the User object can be removed.

You will note that for both the subscription and the plan we need to obtain the Subscription ID and Plan ID respectively. Rather than typing these in I have added the command to obtain it from the name of the Subscription and the Plan.

# query SQL function

function Invoke-SQL {

param([string] $connstring,[string] $sqlCommand)

$connectionString = $connstring

$connection = new-object system.data.SqlClient.SQLConnection($connectionString)

$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)

$connection.Open()

$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command

$dataset = New-Object System.Data.DataSet

$adapter.Fill($dataSet) | Out-Null

$connection.Close()

$dataSet.Tables

}

 

# get WAP connection string

$connstring = (Get-MgmtSvcSetting -Namespace adminsite | where {$_.name -eq ‘ApplicationServicesConnectionstring’}).value

$connstring = $connstring.Split(‘;’)[0] + ‘;’ + $connstring.Split(‘;’)[1] + “; Integrated Security=SSPI”

 

# get WAP websites

$adminsite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$TenantSite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘TenantSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$AuthSite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AuthSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$windowsauthsite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘WindowsAuthSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$AdminAPI = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Microsoft.Azure.Portal.Configuration.OnPremPortalConfiguration.RdfeAdminUri'” -connstring $connstring).value

$TenantAPI = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘TenantSite’ and Name = ‘Microsoft.Azure.Portal.Configuration.AppManagementConfiguration.RdfeUnifiedManagementServiceUri'” -connstring $connstring).value

$ClientRealm = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Authentication.RelyingParty'” -connstring $connstring).value.Split(‘,’)[1].replace(‘”Realm”:”‘,).replace(‘”‘,)

 

# Check is cert is signed

$admin = $adminsite.Split(‘:’)

$cert = !(New-Object System.Net.Security.SslStream((New-Object System.Net.Sockets.TcpClient($admin[1].Replace(‘/’,),$admin[2])).GetStream())).IsSigned

 

# get token

$token = Get-MgmtSvcToken -AuthenticationSite $windowsauthsite -ClientRealm $ClientRealm -Type Windows -DisableCertificateValidation:$cert

 

# Remove Subscription

$SubscriptionName = ‘Steve’

$subscriptionID = (Get-MgmtSvcSubscription -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert | Where-Object {$_.SubscriptionName -eq $SubscriptionName}).SubscriptionID

Remove-MgmtSvcSubscription -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -SubscriptionId $subscriptionID

 

# remove Plan

$planName = ‘steve’

$planid = (Get-MgmtSvcPlan -AdminUri $adminapi -Token $token -DisableCertificateValidation:$cert -DisplayName $planName).id

Remove-MgmtSvcPlan -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -PlanId $planid

 

# remove User

$name = ‘Steve’

Remove-MgmtSvcUser -AdminUri $adminapi -Token $token -DisableCertificateValidation:$cert -Name $name

This concludes the Blog posts on the Simple administrative tasks for Azure Pack, if you have followed each of the blogs that we have found, created and deleted each of the 3 object types, but once the Subscription is associated to a user there are no resources allocated to it. In the next group of Blog posts we will go over the process to allocate resources to the Plan/Subscription. But until then.

Good Luck.

Steve

Windows Azure Pack PowerShell sample commands – Add Commands

Windows Azure Pack PowerShell sample commands – Add Commands

To follow on from my last post where I detailed the process of obtaining the basic details from Azure Pack, for this post I will detail the process to Add objects into Azure Pack, for example new user’s, plans & subscriptions.

In the PowerShell script below, you’ll note that the 33 lines are identical to the first 33 lines of the previous post, this ensures that all of the variables we are using will be accessible, as always with WordPress sites make sure that you check the quotation makes when copying and pasting into the ISE. The last 13 lines we create a user object, the email account supplied does not need to exist when using the native Azure Pack tenant portal authentication, this should be swapped out to ADFS or equivalent authentication solutions when in production.

You will also note that when creating the subscription we obtain a variable called $Plan which we then use the ID from for the new Subscription, this saves us the requirement of remembering the ID which looks something like this: 1fc6dc79 which isn’t very easy to recall.

# query SQL function

function Invoke-SQL {

param([string] $connstring,[string] $sqlCommand)

$connectionString = $connstring

$connection = new-object system.data.SqlClient.SQLConnection($connectionString)

$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)

$connection.Open()

$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command

$dataset = New-Object System.Data.DataSet

$adapter.Fill($dataSet) | Out-Null

$connection.Close()

$dataSet.Tables

}

 

# get WAP connection string

$connstring = (Get-MgmtSvcSetting -Namespace adminsite | where {$_.name -eq ‘ApplicationServicesConnectionstring’}).value

$connstring = $connstring.Split(‘;’)[0] + ‘;’ + $connstring.Split(‘;’)[1] + “; Integrated Security=SSPI”

 

# get WAP websites

$adminsite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$TenantSite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘TenantSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$AuthSite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AuthSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$windowsauthsite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘WindowsAuthSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$AdminAPI = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Microsoft.Azure.Portal.Configuration.OnPremPortalConfiguration.RdfeAdminUri'” -connstring $connstring).value

$TenantAPI = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘TenantSite’ and Name = ‘Microsoft.Azure.Portal.Configuration.AppManagementConfiguration.RdfeUnifiedManagementServiceUri'” -connstring $connstring).value

$ClientRealm = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Authentication.RelyingParty'” -connstring $connstring).value.Split(‘,’)[1].replace(‘”Realm”:”‘,).replace(‘”‘,)

 

# Check is cert is signed

$admin = $adminsite.Split(‘:’)

$cert = !(New-Object System.Net.Security.SslStream((New-Object System.Net.Sockets.TcpClient($admin[1].Replace(‘/’,),$admin[2])).GetStream())).IsSigned

 

# get token

$token = Get-MgmtSvcToken -AuthenticationSite $windowsauthsite -ClientRealm $ClientRealm -Type Windows -DisableCertificateValidation:$cert

 

# Add new User

$Email = ‘steve@laptop.com’ #user email address

$name = ‘Steve’ # Name of user

Add-MgmtSvcUser -AdminUri $AdminAPI -Email $Email -name $name -Token $token -DisableCertificateValidation:$cert

 

# Add Plan

$displayname = ‘steve’ # Name of the Plan

Add-MgmtSvcPlan -AdminUri $adminapi -Token $token -DisableCertificateValidation:$cert -DisplayName $displayname

 

# Add Subscription

$SubscriptionName = ‘Steve’ # Name of the Subscription

$PlanName = ‘Steve’ # Name of the Plan

$Email = ‘steve@laptop.com’ #user email address

$uname = ‘Steve’ # Name of user

$plan = get-MgmtSvcPlan -AdminUri $adminapi -Token $token -DisableCertificateValidation:$cert -DisplayName $Planname

Add-MgmtSvcSubscription -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert -AccountAdminLiveEmailId $Email -AccountAdminLivePuid $uname -PlanId $plan.id -FriendlyName $SubscriptionName

 

Like previously this isn’t an exhaustive list of commands but a starting point.

Good Luck Steve

Windows Azure Pack PowerShell sample commands – Get Commands

Windows Azure Pack PowerShell sample commands – Get Commands

Over the past 12 months I’ve been working less on SCCM and more in Windows Azure Pack, to help integrate a complex database as a service solution. One of the things I have found over this time is there is limited information around how to automate using PowerShell, along with quite a number assumptions made in provided documentation. To this end I have spent a couple of hours writing out some useful PowerShell commands to obtain the commonly used settings of a Azure Pack environment for example the websites and ports which go with each of the websites. I’ll note that while the native Azure Pack PowerShell can obtain this information on a WAP express install, it doesn’t return the information in a dispersed configuration which is the recommended deployment for production usage of Azure Pack. So I have written up the following code to help out everybody.

# query SQL function

function Invoke-SQL {

param([string] $connstring,[string] $sqlCommand)

$connectionString = $connstring

$connection = new-object system.data.SqlClient.SQLConnection($connectionString)

$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)

$connection.Open()

$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command

$dataset = New-Object System.Data.DataSet

$adapter.Fill($dataSet) | Out-Null

$connection.Close()

$dataSet.Tables

}

 

# get WAP connection string

$connstring = (Get-MgmtSvcSetting -Namespace adminsite | where {$_.name -eq ‘ApplicationServicesConnectionstring’}).value

$connstring = $connstring.Split(‘;’)[0] + ‘;’ + $connstring.Split(‘;’)[1] + “; Integrated Security=SSPI”

 

# get WAP websites

$adminsite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$TenantSite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘TenantSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$AuthSite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AuthSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$windowsauthsite = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘WindowsAuthSite’ and Name = ‘Authentication.Fqdn'” -connstring $connstring).value

$AdminAPI = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Microsoft.Azure.Portal.Configuration.OnPremPortalConfiguration.RdfeAdminUri'” -connstring $connstring).value

$TenantAPI = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘TenantSite’ and Name = ‘Microsoft.Azure.Portal.Configuration.AppManagementConfiguration.RdfeUnifiedManagementServiceUri'” -connstring $connstring).value

$ClientRealm = (Invoke-SQL -sqlCommand “SELECT value FROM [Config].[Settings] where Namespace = ‘AdminSite’ and Name = ‘Authentication.RelyingParty'” -connstring $connstring).value.Split(‘,’)[1].replace(‘”Realm”:”‘,).replace(‘”‘,)

 

# Check is cert is signed

$admin = $adminsite.Split(‘:’)

$cert = !(New-Object System.Net.Security.SslStream((New-Object System.Net.Sockets.TcpClient($admin[1].Replace(‘/’,),$admin[2])).GetStream())).IsSigned

 

# get token

$token = Get-MgmtSvcToken -AuthenticationSite $windowsauthsite -ClientRealm $ClientRealm -Type Windows -DisableCertificateValidation:$cert

 

# get admin users

Get-MgmtSvcAdminUser -ConnectionString $connstring.Replace(“PortalConfigStore”,“Store”)

 

# get users

Get-MgmtSvcUser -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert

 

# get plans

Get-MgmtSvcPlan -AdminUri $adminapi -Token $token -DisableCertificateValidation:$cert

 

# get Subscriptions

Get-MgmtSvcSubscription -AdminUri $AdminAPI -Token $token -DisableCertificateValidation:$cert

*make sure you check the quotes when copying the PowerShell commands

While this isn’t an exhaustive list of commands it covers off the basics which will get anybody new to the platform and PowerShell a leg up to obtain the information of the solution.

You will note that I’m using SQL queries to obtain the websites and ports as I found it was more robust to complete the process this way.

Good luck

Steve

Automated updates of Devolution’s Remote Desktop Manager using SCCM

Automated updates of Devolution’s Remote Desktop Manager using SCCM

Recently I have been spending time going back to basic’s around automation in SCCM with PowerShell to brush up on my skills. As part of this I spent a few days playing around with migrating to use Visual Studio as my development space for PowerShell with Adam Driscoll’s PowerShell Tools for Visual Studio add in, which I can’t recommend highly enough when tied to TFS online, Version becomes a super simple task of checking your code in rather than saving different versions.

So I have my Dev space all setup, and went to connect to my Lab Servers Via Devolutions Remote Desktop Manager and got this Prompt

Which annoyed me to no end as I only installed it 3 weeks ago and it appears there had already been 2 version changes. So rather than just disabling the prompt to check for updates as I would normally do on RDM, I started investigating how I can auto mate the updates of RDM without paying for a Third Party tool, and still have the latest version available from my SCCM Server.

So first things first we need to find out how the Application checks to see if there are any updated versions available, for this we can use Fiddler which is a great tool that allows to capture the Http/Https traffic leaving your computer.

Which presented us with these links:

http://remotedesktopmanager.com/products.htm

http://remotedesktopmanager.com/data/RdmChangeHistoryUpdate.htm

From here we browse to each and can see that http://remotedesktopmanager.com/products.htm returns this:

Which we can obtain the current version for each of the versions of RDM, along with where to download the application exe file from. Right about now I was thinking this is going to be a cake walk we can just step through each of the lines in the htm file and select only the ones which we need.

So we grab the web address and use the following script to obtain the htm page into a PowerShell Variable:

$wc = New-Object Net.WebClient

$srdm = $wc.DownloadString(“http://remotedesktopmanager.com/products.htm”)

And when we run $srdm in the ISE to see the list is looks like this:

This should be super easy, I’m still thinking we can use a simple foreach on $srdm like I would on a normal multi line string. I try it, and I get the all of the information back, which I must say threw me for a minute, so I tried

$srdm.count

With the following result:

I dig deeper into $srdm and find that the end of the line is a line break rather than a carriage Return as I was expecting, so after some quick research found that we can split $srdm using $srdm.Split(“`r`n”) which will separate it on the line break’s rather than the carriage returns. You can see if we now run the following command $srdm.Split(“`r`n”).count that the number is vastly larger than 1 which was the previous result

So we can now use the simple foreach to step through each line in the variable, which will look like this:

$RDMVerChk = “RDMEnterprise”

$wc = New-Object Net.WebClient

$srdm = $wc.DownloadString(“http://remotedesktopmanager.com/products.htm”)

$out = @()

foreach($rdm in $srdm.Split(“`r`n”).count)

{

$obj = New-Object PSObject

if ($rdm -like “*$RDMVerChk*”)

{

$s = $rdm.Split(“=”)

$obj | Add-Member Noteproperty Name $s[0].split(“.”)[1]

$obj | Add-Member Noteproperty Value $s[1]

$out += $obj

}

}

You will see that I have also added a check only return the strings which contain RDMEnterprise as this is the version of RDM we use, you can change the $RDMVerChk variable to the required version. The next step is to create new PSobject to capture the results into a simple Array for use later in the script.

Now the next step is to check in SCCM to check if the version available from the RDM website is currently available, and if it isn’t to publish it.

To start we need to import the Configuration Manager PowerShell module with the following command:

Import-Module $env:SMS_ADMIN_UI_PATH.Replace(“i386”,“ConfigurationManager.psd1”) -Force

Once imported we need to set the current PowerShell location to the SCCM SIteCode for this example P01

Set-Location “P01:”

Now we can completed a simple Get-CMApplication to see if the RDM Version exists, we will use the expected name of the application we create during running this script which is “Remote_Desktop_Manager-$rdmver where $rdmver is the RDM version from the Website.

$rdmver = $out | ForEach-Object {if ($_.Name -eq “Version”){$_.Value}}

if((Get-CMApplication -Name “Remote_Desktop_Manager-$rdmver))

{

write-output “Already Available”

}

else

{

write-output “Needs to be created”

}

Great now we just need to add the application to SCCM, and make it available to users via the Application Catalog. To do this we use the following script:

#download File

$wc.DownloadFile(($out | ForEach-Object {if ($_.Name -eq “exe”){$_.Value}}), $path\RDM.exe”)

#Create RDM folder and Extract MSI from exe

if(!(Test-Path -path $Path\RDM”)){New-Item -Path $Path\RDM” -Force -ItemType Directory | Out-Null}

Start-Process -FilePath $path\RDM.exe” -ArgumentList “/extract:$path\RDM” -Wait

# Extract .ico file from the RDM.exe

[System.Drawing.Icon]::ExtractAssociatedIcon($path\RDM.exe”).ToBitmap().save($path\RDM\RDM.ico”)

$path = $path\RDM”

$InstallFile = (Get-ChildItem -Path $path | where {$_.name -Like “*.msi”}).name

# Create Folders and move files around

if(!(Test-Path -path $DestPath\applications”)){New-Item -Path $DestPath\applications” -Force -ItemType Directory | Out-Null}

if(!(Test-Path -path $DestPath\applications\$prodman)){New-Item -Path $DestPath\applications\$prodman -Force -ItemType Directory | Out-Null}

if(!(Test-Path -path $DestPath\applications\$prodman\$prodname$rdmver)){New-Item -Path $DestPath\applications\$prodman\$prodname$rdmver -Force -ItemType Directory | Out-Null}

Copy-Item $path\*” $DestPath\applications\$prodman\$prodname$rdmver

# Create SCCM Application

Set-Location $Sitecode`:”

# get existing Deployment Types to be SuperSeeded

$oldappdt = Get-CMDeploymentType -ApplicationName $prodname*”

$newapp = New-CMApplication -Name $prodname$rdmver -Publisher ($prodman.replace(” “,“_”)) -SoftwareVersion $rdmver -IconLocationFile $path\rdm.ico” -LocalizedApplicationName ($prodname.replace(” “,“_”))

Add-CMDeploymentType -MsiInstaller -InstallationFileLocation $DestPath\applications\$prodman\$prodname$rdmver\$installfile -Application $newapp -ForceForUnknownPublisher:$true

# Create SuperSeedence for each existing Deployment Type

foreach ($dt in $oldappdt)

{

# You need to get the new Deployment Type each time as it changes when new SuperSeedences are added.

$appdt = Get-CMDeploymentType -ApplicationName $prodname$rdmver

Add-CMDeploymentTypeSupersedence -SupersedingDeploymentType $dt -IsUninstall $true -SupersededDeploymentType $appdt

}

# Disribution Content to a DP (this can be changed to DP Group as needed)

Start-CMContentDistribution -Application $newapp -DistributionPointName $DP

# Create New User collection for deployment, defineing the limiting collection and the collection to include (Can be changed to Query As required)

$newcol = New-CMUserCollection -Name “Install $ProdMan $Prodname $rdmver -LimitingCollectionName $LimitingCol

Add-CMUserCollectionIncludeMembershipRule -Collection $newcol -IncludeCollectionName $IncCol

# deploy New Application to New Collection

Start-CMApplicationDeployment -CollectionName ($newcol.Name) -Name ($newapp.LocalizedDisplayName)

As you can see it is pretty much broken up into 3 phases, Download & Prepare the file, Confirm if folders exist on Media Store & copy extracted files up to the new folders, And create SCCM Application, Supersede old versions & deploy the new application to a New Collection. I will focus on the first 2 phase’s as they contain the interesting parts, while the last phase is using the default create collection & deployment commands for SCCM.

Download phase, we obtain a copy of the file using this command:

$wc.DownloadFile(($out | ForEach-Object {if ($_.Name -eq “exe”){$_.Value}}), $path\RDM.exe”)

Where $wc & $out still exist from earlier use scripts

The next step is to create a scratch folder to target the exe’s /Extract command at, this will give us the following 2 files

The next step is to create the .ico we can use in the Application Catalog to make it look pretty for the end user

[System.Drawing.Icon]::ExtractAssociatedIcon($path\RDM.exe”).ToBitmap().save($path\RDM\RDM.ico”)

Which we are saving into the scratch folder to copy up to the SCCM server. We also define $InstallFile

From the MSI that exists in the scratch folder rather than using assuming that Devolutions will continue to use the same naming convention for the msi file.

The creation of the folders is pretty self-explanatory.

The next phase is to create the SCCM Applications & supersede the old versions.

We obtain the list of existing Application Deployment Types as the first step to ensure it doesn’t include the application we are creating.

With New-CMApplication in addition to the mandatory parameters we also see -LocalizedApplicationName & -IconLocationFile these are used to define the appearance of the application when published in the Application Catalog, the IconLocationFile parameter have the requirement of either a .ico or image file to work, while LocalizedApplicationName is a free text field.

With Add-CMDeploymentType we have included the following parameter -ForceForUnknownPublisher:$true which ensure that if the MSI file is not signed it will still add the deployment type, You should only use this on MSI’s you know & trust the origin of.

To Supersede the existing versions of Remote Desktop Manager we are using the Add-CMDeploymentTypeSupersedence command, as noted above, every time you add a new Superseded deployment type to the new Deployment type you will need reload the variable as it has changed.

So that’s the breakdown, the Whole script looks like this (Make sure you fix the Quotes when copying from the internet when not using PowerShell 5):

Set-Location “c:”

$Sitecode = “” #SCCM SiteCode

$path = “C:\data”

$Destpath = “” #Path to Central media store eg: \\<servername>\Source$

$dp = “” #FQDN of DP to deploy too (single DP at this point)

$LimitingCol = “” # name of limiting Collection

$IncCol = “” # name of collection to include

$RDMVerChk = “RDMEnterprise” # version of RDM

Import-Module $env:SMS_ADMIN_UI_PATH.Replace(“i386”,“ConfigurationManager.psd1”) -Force | Out-Null

$curdrv = (Get-Location).path

$wc = New-Object Net.WebClient

$srdm = $wc.DownloadString(“http://remotedesktopmanager.com/products.htm”)

$out = @()

foreach($rdm in $srdm.Split(“`r`n”))

{

$obj = New-Object PSObject

if ($rdm -like “*$RDMVerChk*”)

{

$s = $rdm.Split(“=”)

$obj | Add-Member Noteproperty Name $s[0].split(“.”)[1]

$obj | Add-Member Noteproperty Value $s[1]

$out += $obj

}

}

$rdmver = $out | ForEach-Object {if ($_.Name -eq “Version”){$_.Value}}

Set-Location $Sitecode`:”

if((Get-CMApplication -Name “Remote_Desktop_Manager-$rdmver))

{

Set-Location $curdrv

}

else

{

Set-Location $curdrv

#download File

$wc.DownloadFile(($out | ForEach-Object {if ($_.Name -eq “exe”){$_.Value}}), $path\RDM.exe”)

#Create RDM folder and Extract MSI from exe

if(!(Test-Path -path $Path\RDM”)){New-Item -Path $Path\RDM” -Force -ItemType Directory | Out-Null}

Start-Process -FilePath $path\RDM.exe” -ArgumentList “/extract:$path\RDM” -Wait

# Extract .ico file from the RDM.exe

[System.Drawing.Icon]::ExtractAssociatedIcon($path\RDM.exe”).ToBitmap().save($path\RDM\RDM.ico”)

$path = $path\RDM”

$InstallFile = (Get-ChildItem -Path $path | where {$_.name -Like “*.msi”}).name

# Create Folders and move files around

if(!(Test-Path -path $DestPath\applications”)){New-Item -Path $DestPath\applications” -Force -ItemType Directory | Out-Null}

if(!(Test-Path -path $DestPath\applications\$prodman)){New-Item -Path $DestPath\applications\$prodman -Force -ItemType Directory | Out-Null}

if(!(Test-Path -path $DestPath\applications\$prodman\$prodname$rdmver)){New-Item -Path $DestPath\applications\$prodman\$prodname$rdmver -Force -ItemType Directory | Out-Null}

Copy-Item $path\*” $DestPath\applications\$prodman\$prodname$rdmver

# Create SCCM Application

Set-Location $Sitecode`:”

# get existing Deployment Types to be SuperSeeded

$oldappdt = Get-CMDeploymentType -ApplicationName $prodname*”

$newapp = New-CMApplication -Name $prodname$rdmver -Publisher ($prodman.replace(“_”,” “)) -SoftwareVersion $rdmver -IconLocationFile $path\rdm.ico” -LocalizedApplicationName ($prodname.replace(“_”,” “))

Add-CMDeploymentType -MsiInstaller -InstallationFileLocation $DestPath\applications\$prodman\$prodname$rdmver\$installfile -Application $newapp -ForceForUnknownPublisher:$true

# Create SuperSeedence for each existing Deployment Type

foreach ($dt in $oldappdt)

{

# You need to get the new Deployment Type each time as it changes when new SuperSeedences are added.

}

# Disribution Content to a DP (this can be changed to DP Group as needed)

Start-CMContentDistribution -Application $newapp -DistributionPointName $DP

# Create New User collection for deployment, defineing the limiting collection and the collection to include (Can be changed to Query As required)

$newcol = New-CMUserCollection -Name “Install $ProdMan $Prodname $rdmver -LimitingCollectionName $LimitingCol

Add-CMUserCollectionIncludeMembershipRule -Collection $newcol -IncludeCollectionName $IncCol

# deploy New Application to New Collection

Start-CMApplicationDeployment -CollectionName ($newcol.Name) -Name ($newapp.LocalizedDisplayName)

# return to original Path

Set-Location $curdrv

}

Good Luck

Steve

Create a Device Collection based on a User Collection

Create a Device Collection based on a User Collection

I had an interesting discussion with a past colleague the other day where he was asking around to find out if it was possible to create a Device Collection based off a User Collection using the Primary Device option. After a bit of back and forth and him providing the query he was using for the user collection we started playing around with SubSelect queries to see if we can even reference User objects in the device collection.

So to start off we create the simple user query of get user x when in domain y like so:

select *

from SMS_R_User

where SMS_R_User.UserPrincipalName Like ‘%hosking%’

and SMS_R_USER.WindowsNTDomain like ‘%domain%’

Which will return my user account into a User Collection let’s call “Steves User Account” with a collection ID of “P0100024” from here we will move over to the device collection area of the console and create a collection called “Steve Primary Computers” and for this collection we will use the following query:

select *

from SMS_R_System

where SMS_R_System.resourceID in

(select SMS_UserMachineRelationship.resourceID

from SMS_R_User

join SMS_UserMachineRelationship on SMS_UserMachineRelationship.UniqueUserName = SMS_R_User.UniqueUserName

where SMS_R_User.UserPrincipalName Like ‘%hosking%’

and SMS_R_USER.WindowsNTDomain like ‘%domain%’)

So what we are doing here is using the SMS_UserMachineRelationship table which holds the information for Primary Computers for each user, and limiting the search for my username and Domain, which we then return the ResourceID, and finally we then return the SMS_R_system Resources which are in the query into the Device collection.

So this works great, but it does mean that I now have the same query in 2 different locations so if I want to change the query in the User Collection, I will then need to update the query in the Computer Collection.

So we looked at just including the User Collection itself in the query, this information is actually captured in a table accessible via WMI in tables that share this type of naming convention: SMS_CM_RES_COLL_<CollectionID> so from here we wrote this query:

select sms_r_system.name

from SMS_R_System

where SMS_R_System.resourceID in

(select SMS_UserMachineRelationship.resourceID

from SMS_CM_RES_COLL_P0100024

join SMS_UserMachineRelationship on SMS_UserMachineRelationship.UniqueUserName = SMS_CM_RES_COLL_P0100024.SMSID)

This query is even easy then the middle one as all we are doing using the UniqueUserName field from the SMS_UserMachineRelationship table and joining it with the SMSID from the collection table and returning the ResourceID like we did in the previous query.

Good Luck

Steve