Browsed by
Tag: PoSH

PowerShell GraphAPI

PowerShell GraphAPI

In the last blog post we went over what the GraphAPI is and why it’s all powerful, with this post we are going to go over how you can leverage the GraphAPI via PowerShell.
First things first we need to register an application at https://apps.dev.microsoft.com/ this is pretty straight forward, just make sure you save the Client Secret when you create it as you will only see it once! the other thing to note is that you need add a platform as “Web” this address does not need to resolve, nor does your PowerShell Scripts need to use it.
The other important part on this page is to allocate permissions to the application which all depends upon what you want to interrogate the GraphAPI, for more information have a look here where Microsoft has documented the process in more detail then I have.

So we now have the Application ID from the new application, the Client Secret, and the Redirect Address, we can now create a simple PowerShell script like the below (update the Variables to match your environment).

Once you have updated the Script to match your environment you can now execute it and you should see something like this:

@odata.context : https://graph.microsoft.com/beta/$metadata#users/$entity
id : 2f2916e5-953c-4c5d-9f52-bd5db8131d49
accountEnabled : True
assignedLicenses : {@{disabledPlans=System.Object[]; skuId=c7df2760-2c81-4ef7-b578-5b5392b571df}, @{disabledPlans=System.Object[]; skuId=061f9ace-7d42-4136-88ac-31dc755f143f}}
assignedPlans : {@{assignedDateTime=2017-01-18T19:43:48Z; capabilityStatus=Enabled; service=SharePoint; servicePlanId=e95bec33-7c88-4a70-8e19-b10bd9d0c014}, @{assignedDateTime=2017-01-18T19:43:48Z; capabilityStatus=Enabled;
service=SharePoint; servicePlanId=5dbe027f-2339-4123-9542-606e4d348a72}, @{assignedDateTime=2017-01-18T19:43:48Z; capabilityStatus=Enabled; service=exchange; servicePlanId=efb87545-963c-4e0d-99df-69c6916d9eb0},
@{assignedDateTime=2017-01-18T19:43:48Z; capabilityStatus=Enabled; service=MicrosoftCommunicationsOnline; servicePlanId=0feaeb32-d00e-4d66-bd5a-43b5b83db82c}…}
businessPhones : {}
city :
companyName :
country :
department :
displayName : Steven Hosking
givenName : Steven
jobTitle :
mail : mail@AusIgnite2017.onmicrosoft.com
mailNickname : steve
mobilePhone :
onPremisesImmutableId :
onPremisesLastSyncDateTime :
onPremisesSecurityIdentifier :
onPremisesSyncEnabled :
passwordPolicies :
passwordProfile :
officeLocation :
postalCode :
preferredLanguage : en-AU
provisionedPlans : {@{capabilityStatus=Enabled; provisioningStatus=Success; service=exchange}, @{capabilityStatus=Enabled; provisioningStatus=Success; service=exchange}, @{capabilityStatus=Enabled; provisioningStatus=Success;
service=exchange}, @{capabilityStatus=Enabled; provisioningStatus=Success; service=exchange}…}
proxyAddresses : {SMTP:mail@AusIgnite2017.onmicrosoft.com}
refreshTokensValidFromDateTime : 2017-01-18T07:01:55Z
showInAddressList :
state :
streetAddress :
surname : Hosking
usageLocation : AU
userPrincipalName : mail@AusIgnite2017.onmicrosoft.com
userType : Member

If you want to test other options you can change the URL on Line 2 of the script “$uri = “https://graph.microsoft.com/beta/me/”” to query anything which has been published via the GraphAPI, you can find more information here and browsing through the options on the left of the page.

You can also find all of the references of the Beta calls for the GraphAPI which at the moment includes all of the calls for Intune, these might not work on your subscription today, these GraphAPI calls will be green lit for your subscription once it has been migrated over to the Ibiza portal (portal.azure.com).

Good Luck

Steve

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

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

Capturing installed Windows Features from Client OS into SCCM without touching the MOF

Capturing installed Windows Features from Client OS into SCCM without touching the MOF

Recently I was asked how to capture the Enabled Windows features from client machines, to help identify the crazy cats who have Hyper-V turned on just so they can run an extra OS or 2 on there Surface Pro 3. So this one was an interesting question in the server OS there is the Win32_ServerFeatures WMI class which is great on servers, but doesn’t exist on client OS’s. All of the information is captured in DSIM right, but how do you turn that into a WMI Class for reporting purposes.

I started off by creating a PowerShell Script with plans to run it in DCM to create the Registry keys which we have traditionally used to capture the information in SCCM, which looks like this:

$f = “”

$feature = dism /online /get-features

foreach ($svc in $feature)

{

if ($svc -like “*Feature Name *”)

{

$f = $f + $svc.Replace(“Feature Name : “, “”) + “,”

}

elseif ($svc -like “*State : *”)

{

$f = $f + $svc.Replace(“State : “,“”) + “;”

}

}

 

Push-Location

Set-Location HKLM:

if ((Test-Path .\software\clientFeatures) -eq $false){New-Item -Path .\software -name clientFeatures | out-null}

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

New-ItemProperty .\software\clientFeatures -Name $feat -Value $featset -PropertyType “string” -Force | Out-Null

$feat = “”

$featset = “”

}

}

Pop-Location

So let’s breakdown the script and explain what each of the sections are doing

$feature = dism /online /get-features

This puts the default DISM result into the $feature variable

I know your sitting there like me and wondering how you can turn that into a registry entry.

Well that’s where the next phase of the script comes in and creates a single string

foreach ($svc in $feature)

{

if ($svc -like “*Feature Name *”)

{

$f = $f + $svc.Replace(“Feature Name : “, “”) + “,”

}

elseif ($svc -like “*State : *”)

{

$f = $f + $svc.Replace(“State : “,“”) + “;”

}

}

Which steps through each of the lines in the result of the DISM command, and finds the lines which have “Feature Names” and “State” then writes into string called $f which we will use later. As we have put the , after the Feature name and the ; after the state, we can then split these in the next phase of the script. Which I will now explain what we are doing there:

Push-Location

Set-Location HKLM:

if ((Test-Path .\software\clientFeatures) -eq $false){New-Item -Path .\software -name clientFeatures | out-null}

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

New-ItemProperty .\software\clientFeatures -Name $feat -Value $featset -PropertyType “string” -Force | Out-Null

$feat = “”

$featset = “”

}

}

Pop-Location

At a high level we are connecting to HKEY_LOCAL_MACHINE and created a new Key called ClientFeatures under the Software key, then creating a new REG_SZ for each of the features with the states as the value of the key. At this point I was really happy I had all of the features in the registry in a format I could then create a MOF from, this was short lived as I noticed I had over 100 features in the client OS which would create a heartache when creating the MOF file as it would be around 300 lines of boring code. So I went to myself, self why don’t you just create the WMI class with PowerShell then you don’t have to worry about getting the MOF file right, which I responded to self with that’s a great idea I’m amazed I didn’t think of it myself. Below you will see the resulting script:

if ((Get-WmiObject -Class Win32_ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -lt 1)

{

$newClass = New-Object System.Management.ManagementClass(“root\cimv2”, [String]::Empty, $null)

$newClass[“__CLASS”] = “Win32_ClientFeatures”

$newClass.Qualifiers.Add(“Static”, $true)

$newClass.Properties.Add(“key”,[System.Management.CimType]::String, $false)

$newClass.Properties[“key”].Qualifiers.Add(“Key”, $true)

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

$newClass.Properties.Add($feat,[System.Management.CimType]::String, $false)

$newClass.Put() | Out-Null

$feat = “”

$featset = “”

}

}

}

if ((Get-WmiObject -Class Win32_ClientFeatures).__path -eq $null){$new = $true} else {$inst = (Get-WmiObject -Class Win32_ClientFeatures).key}

if ($new) {$classinstance = $newClass.CreateInstance()}

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

if ($new)

{

$classinstance.$feat = $featset

$classinstance.put() | Out-Null

}

else

{

$fe = Get-WmiObject -Class Win32_clientFeatures

$fe.$feat = $featset

$fe.put() | Out-Null

}

$feat = “”

$featset = “”

}

}

Let’s dive into the breakdown of the slab of code,

if ((Get-WmiObject -Class Win32_ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -lt 1)

This if statement is checking to see if the WMI Class called Win32_ClientFeatures has any properties assigned to it, if the count is less than 1 then we will go ahead and create the WMI Class and create a Property for each of the Windows Features which is this slab of code:

{

$newClass = New-Object System.Management.ManagementClass(“root\cimv2”, [String]::Empty, $null)

$newClass[“__CLASS”] = “Win32_ClientFeatures”

$newClass.Qualifiers.Add(“Static”, $true)

$newClass.Properties.Add(“key”,[System.Management.CimType]::String, $false)

$newClass.Properties[“key”].Qualifiers.Add(“Key”, $true)

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

$newClass.Properties.Add($feat,[System.Management.CimType]::String, $false)

$newClass.Put() | Out-Null

$feat = “”

$featset = “”

}

}

}

We have also created a key called “key” inventive I know, but it works. So we now have a WMI Class with all of the properties the next step is to create a WMI instance of the state of each of the Windows Features like so:

if ((Get-WmiObject -Class Win32_ClientFeatures).__path -eq $null){$new = $true} else {$inst = (Get-WmiObject -Class Win32_ClientFeatures).key}

if ($new) {$classinstance = $newClass.CreateInstance()}

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

if ($new)

{

$classinstance.$feat = $featset

$classinstance.put() | Out-Null

}

else

{

$fe = Get-WmiObject -Class Win32_clientFeatures

$fe.$feat = $featset

$fe.put() | Out-Null

}

$feat = “”

$featset = “”

}

}

The if statement is checking to see if the there is an existing instance in the WMI object and selects that instance to update, otherwise we will go ahead and create a new instance. As you can see in the script these are 2 different groups of code.

So I now have a great WMI class which I can query with PowerShell and it returns like so:

We are now sitting there really happy with our new WMI class which captures all of the Windows Features settings for the computer great. The next step is to import the new WMI class into SCCM Hardware Inventory, and this is where it became interesting, as I got this error message:

I started doing some digging into this and found that the Remote Server Administration Tools features all started the same along with the language packs, so I tweaked the script to handle this and is still didn’t correct the issue. At about this point I came to the realisation that as easy as it would be from a reporting point of view I would be next to impossible to maintain as each OS version has different features, and then you need to include the RSAT features, and Language packs, which would result of in hundreds of properties required in WMI for each OS just to capture the information.

To get around this we then need to change the WMI class to have 2 properties called Feature which we will make as the Key for the instances, and value which will contain the state for the Feature. To complete this we run the following script:

$f = “”

$new = $true

$feature = dism /online /get-features

foreach ($svc in $feature)

{

if ($svc -like “*Feature Name *”)

{

$f = $f + $svc.Replace(“Feature Name : “, “”) + “,”

}

elseif ($svc -like “*State : *”)

{

$f = $f + $svc.Replace(“State : “,“”) + “;”

}

}

if ((Get-WmiObject -Class ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -lt 1)

{

$newClass = New-Object System.Management.ManagementClass(“root\cimv2”, [String]::Empty, $null)

$newClass[“__CLASS”] = “Win32_ClientFeatures”

$newClass.Qualifiers.Add(“Static”, $true)

$newClass.Properties.Add(“Feature”,[System.Management.CimType]::String, $false)

$newClass.Properties[“Feature”].Qualifiers.Add(“Key”, $true)

$newClass.Properties.Add(“value”,[System.Management.CimType]::String, $false)

$newClass.Put() | out-null

}

 

foreach ($r in $f.Split(“;”))

{

 

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

if ((Get-WmiObject -Class Win32_ClientFeatures).feature -eq $feat)

{

$fe = Get-WmiObject -query “select * from win32_clientFeatures where feature = ‘$feat‘”

$fe.value = $featset

$fe.put() | Out-Null

}

else

{

$classinstance = $newClass.CreateInstance()

$classinstance.feature = $feat

$classinstance.value = $featset

$classinstance.put() | Out-Null

}

$feat = “”

$featset = “”

}

}

The first part is the same as above, but once we get to defining the WMI class it changes a little, now rather than stepping through each of windows features and creating a property for each feature we are now just creating 2 properties called “Feature” and “Value”, only if the WMI class doesn’t exist, as defined in the below script:

if ((Get-WmiObject -Class ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -lt 1)

{

$newClass = New-Object System.Management.ManagementClass(“root\cimv2”, [String]::Empty, $null)

$newClass[“__CLASS”] = “Win32_ClientFeatures”

$newClass.Qualifiers.Add(“Static”, $true)

$newClass.Properties.Add(“Feature”,[System.Management.CimType]::String, $false)

$newClass.Properties[“Feature”].Qualifiers.Add(“Key”, $true)

$newClass.Properties.Add(“value”,[System.Management.CimType]::String, $false)

$newClass.Put() | out-null

}

The next step is to create a new instance for each features, defining the feature name and the state like so:

foreach ($r in $f.Split(“;”))

{

 

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

if ((Get-WmiObject -Class Win32_ClientFeatures).feature -eq $feat)

{

$fe = Get-WmiObject -query “select * from win32_clientFeatures where feature = ‘$feat‘”

$fe.value = $featset

$fe.put() | Out-Null

}

else

{

$classinstance = $newClass.CreateInstance()

$classinstance.feature = $feat

$classinstance.value = $featset

$classinstance.put() | Out-Null

}

$feat = “”

$featset = “”

}

}

We are also checking to see if the instance already exists for the feature, if it does exist, we will need to update the value, otherwise we create a new class instance.

Now when we import the WMI class into the SCCM console it doesn’t throw an error, and we can see the v_GS_ClientFeatures in SQL Reporting Services as an option to report upon now, so how do I get the list of computers which have Hyper-V Enabled, well you create a SQL Report with the following query:

select rsys.Name0

from v_R_System as rsys join

v_GS_CLIENT_FEATURES as feat on rsys.ResourceID = feat.ResourceID

where Feature0 = ‘Microsoft-Hyper-V’ and

value0 = ‘enabled’

And the WMI Query for a collection looks like this:

select * from SMS_R_System inner join SMS_G_System_CLIENT_FEATURES on SMS_G_System_CLIENT_FEATURES.ResourceId = SMS_R_System.ResourceId where SMS_G_System_CLIENT_FEATURES.Feature = “Microsoft-Hyper-V” and SMS_G_System_CLIENT_FEATURES.value = “Enabled”

The DCM detection PowerShell script looks like this:

$compliance = “Compliant”

$f = “”

$feature = dism /online /get-features

foreach ($svc in $feature)

{

if ($svc -like “*Feature Name *”)

{

$f = $f + $svc.Replace(“Feature Name : “, “”) + “,”

}

elseif ($svc -like “*State : *”)

{

$f = $f + $svc.Replace(“State : “,“”) + “;”

}

}

if ((Get-WmiObject -Class win32_ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -eq 2)

{

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

$cls = Get-WmiObject -query “select * from win32_clientFeatures where feature = ‘$feat‘”

if ($cls.value -ne $featset) {$compliance = “Non-Compliant”}

$feat = “”

$featset = “”

}

}

}

else

{

$compliance = “Non-Compliant”

}

$compliance

And the DCM remediation PowerShell script looks like this:

$f = “”

$new = $true

$feature = dism /online /get-features

foreach ($svc in $feature)

{

if ($svc -like “*Feature Name *”)

{

$f = $f + $svc.Replace(“Feature Name : “, “”) + “,”

}

elseif ($svc -like “*State : *”)

{

$f = $f + $svc.Replace(“State : “,“”) + “;”

}

}

if ((Get-WmiObject -Class ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -lt 1)

{

$newClass = New-Object System.Management.ManagementClass(“root\cimv2”, [String]::Empty, $null)

$newClass[“__CLASS”] = “Win32_ClientFeatures”

$newClass.Qualifiers.Add(“Static”, $true)

$newClass.Properties.Add(“Feature”,[System.Management.CimType]::String, $false)

$newClass.Properties[“Feature”].Qualifiers.Add(“Key”, $true)

$newClass.Properties.Add(“value”,[System.Management.CimType]::String, $false)

$newClass.Put() | out-null

}

 

foreach ($r in $f.Split(“;”))

{

 

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

if ((Get-WmiObject -Class Win32_ClientFeatures).feature -eq $feat)

{

$fe = Get-WmiObject -query “select * from win32_clientFeatures where feature = ‘$feat‘”

$fe.value = $featset

$fe.put() | Out-Null

}

else

{

$classinstance = $newClass.CreateInstance()

$classinstance.feature = $feat

$classinstance.value = $featset

$classinstance.put() | Out-Null

}

$feat = “”

$featset = “”

}

}

$compliance = “Compliant”

$compliance

For the DCM you will need to do a detection on a string that equals “Compliant” and remediate if it not.

As an recap of the whole blog, you can see there is a couple of ways to capture the information, depending upon the amount of properties it might make sense to have a single WMI instance in the WMI class, but in other cases it might make sense to have a large number of WMI instances in the WMI class, each have their advantages and disadvantages for the methods. The nice part of this solution is we don’t need to worry about making any changes to the MOF which makes life so much easier.

Good Luck

Steve

WMI and PowerShell v4 a match made in heaven

WMI and PowerShell v4 a match made in heaven

Natively Windows has a tool to access and view the WMI Namespaces, called WBEMtest.exe. In my opinion it can be quite clunky to use, especially when you are starting to learn WMI. There are multiple WMI Explorer’s out there that do a much better job of presenting the NameSpaces, Classes, Properties & Methods (personally, I use WMI Explorer from Sapien).

Another option is to use Window’s PowerShell to access the WMI objects.

You ask, why would I move from a GUI to a CLI for something like this? Answer: Because it is quicker, especially if it is something you query, quite often you can just simply save the query. It’s hard to save where you need to go in a GUI.

For this blog I’m going to discuss the 2 primary PowerShell cmdlets for WMI which are Get-WmiObject and Invoke-WmiMethod.

Get-WmiObject is used to get a WMIObject be it a NameSpace or a Class. You can also run a WQL Query using this same CmdLet.

An example would be Get-WmiObject -Class win32_bios which will return the following:

Again I hear well that’s great Steve you know to get that information you need to grab the win32_bios class, how the heck do I find what I’m looking for in PowerShell?

Well since PowerShell 3.0 there is a great parameter added to the Out-GridView cmdlet called PassThru. What this allows us to do is select the result we want from the gridview, and do something with it. As a script you could do something like this;

$wmi = Get-WmiObject -List | Out-GridView -passthru
Get-WmiObject -Class $wmi.Name 

 When we run the Script a box will appear like below, in the filter box you can treat this like a search;

When we search for Bios the list will be filtered down to only objects which contain BIOS, we can then select Win32_bios and click ok which places the whole Win32_BIOS object as $WMI;

And the final line is to get the WMI object we have selected;

Invoke-WmiMethod is used to invoke a method that is attached to a Class or Namespace, this one has a little bit more of a niche use case, but is very powerful.

For example we can use the following line to start an application like WBEMTest.exe;

Invoke-WmiMethod -class win32_process -name create -ArgumentList “wbemtest.exe”

As you can see WBEMTest will then appear on the screen.

You can find methods out the same way as you find classes with the following line of code;

$wmi = Get-WmiObject -List | Out-GridView -passthru
Get-WmiObject -Class $wmi.Name | Get-Member -MemberType Methods

 

So the same box as earlier will appear when we run the above lines;


In this example we have searched for Win32_process as it has Methods attached as you can see in the list

Once you select win32_process and Ok you will get the below results.

Ok so we have now used the Get-WmiObject and Invoke-WmiMethod
commandlets separately, let’s join them together and show how there is 2 ways to invoke the methods.

So in this example we are going to start WBEMTest.exe again and then close it by using the Win32_process Methods, the script looks like this;

$wbem = Invoke-WmiMethod -class win32_process -name create -ArgumentList
“wbemtest.exe”
$procid = $wbem.ProcessID
$res = Get-WmiObject -Query “Select * from win32_process where ProcessID = $procid
$res
$res.Terminate() 

 

So as you can see we are defining $wbem
with the result of Invoke-WmiMethod
which is creating a process for WBEMTest.exe, this allows us to capture the ProcessID which we then define as $procid on the next line.

From here we are querying the WMIObject Win32_Process where ProcessID = $procid, which will return the results of the process we just created into the $res variable.

We then return the $res
variable onto the screen to show that the script is doing something.

And the last line we actually close the WBEMtext.exe process we created in the first line. Which as you can see from the list of methods for Win32_process Terminate exists there.

Good Luck

Steve