WAPTenantPublicAPI/WapTenantPublicAPI.psm1
#requires -version 3 Add-Type -AssemblyName 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Add-Type -AssemblyName 'System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' $PublicTenantAPIUrl = $null $Port = $null $IgnoreSSL = $false $Token = $null $Headers = $null $Subscription = $null $OriginalCertificatePolicy = [System.Net.ServicePointManager]::CertificatePolicy function IgnoreSSL { $Provider = New-Object -TypeName Microsoft.CSharp.CSharpCodeProvider $null = $Provider.CreateCompiler() $Params = New-Object -TypeName System.CodeDom.Compiler.CompilerParameters $Params.GenerateExecutable = $False $Params.GenerateInMemory = $True $Params.IncludeDebugInformation = $False $Params.ReferencedAssemblies.Add('System.DLL') > $null $TASource=@' namespace Local.ToolkitExtensions.Net.CertificatePolicy { public class TrustAll : System.Net.ICertificatePolicy { public TrustAll() {} public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) { return true; } } } '@ $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) $TAAssembly=$TAResults.CompiledAssembly ## We create an instance of TrustAll and attach it to the ServicePointManager $TrustAll = $TAAssembly.CreateInstance('Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll') [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll } function TestJWTClaimNotExpired { param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [String] $Token ) #based on functions by Shriram MSFT found on technet: https://gallery.technet.microsoft.com/JWT-Token-Decode-637cf001 process { try { if ($Token.split('.').count -ne 3) { throw 'Invalid token passed, run Get-WAPToken to fetch a new one' } $TokenData = $token.Split('.')[1] | ForEach-Object -Process { $data = $_ -as [String] $data = $data.Replace('-', '+').Replace('_', '/') switch ($data.Length % 4) { 0 { break } 2 { $data += '==' } 3 { $data += '=' } default { throw New-Object -TypeName ArgumentException -ArgumentList ('data') } } [System.Text.Encoding]::UTF8.GetString([convert]::FromBase64String($data)) | ConvertFrom-Json } #JWT Reference Time $Ref = [datetime]::SpecifyKind((New-Object -TypeName datetime -ArgumentList ('1970',1,1,0,0,0)),'UTC') #UTC time right now - Reference time gives amount of seconds to check against $CheckSeconds = [System.Math]::Round(([datetime]::UtcNow - $Ref).totalseconds) if ($TokenData.exp -gt $CheckSeconds) { Write-Output -InputObject $true } else { Write-Output -InputObject $false } } catch { Write-Error -ErrorRecord $_ } } } function PreFlight { [CmdletBinding()] param ( [Switch] $IncludeConnection, [Switch] $IncludeSubscription ) Write-Verbose -Message 'Validating Token Acquired' if (($null -eq $Token) -or ($null -eq $Headers)) { throw 'Token was not acquired, run Get-WAPToken first!' } Write-Verbose -Message 'Validating Token not expired' if (!(TestJWTClaimNotExpired -Token $Token)) { throw 'Token has expired, fetch a new one!' } if ($IncludeConnection) { Write-Verbose -Message 'Validating if connection is set' if ($null -eq $PublicTenantAPIUrl) { throw 'No connection has been made to API yet, run Connect-WAPAPI first!' } } if ($IncludeSubscription) { Write-Verbose -Message 'Validating if subscription is selected' if ($null -eq $Subscription) { throw 'No Subscription has been selected yet, run Select-WAPSubscription first!' } } } function Get-WAPToken { <# .SYNOPSIS Retrieves a Bearer token from either ADFS or the WAP ASP.Net STS. .PARAMETER Url The URL of either the ADFS or WAP STS. .PARAMETER Port The Port on which ADFS or WAP STS is listening. Default for ADFS is 443, for WAP STS 30071. .PARAMETER ClientRealm The realm name of either the TenantSite (default) or AdminSite. .PARAMETER Credential Credentials to acquire the bearer token. .PARAMETER ADFS When enabled the token will be requested from an ADFS STS. When disabled the WAP STS is assumed. .PARAMETER IgnoreSSL When using self-signed certificates, SSL validation will be ignored when this switch is enabled. .EXAMPLE PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS This will return a bearer token from ADFS STS. .EXAMPLE PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.wap.com' -Port 443 This will return a bearer token from WAP STS using the non default port 443. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $URL, [int] $Port, [ValidateSet('http://azureservices/AdminSite','http://azureservices/TenantSite')] [String] $ClientRealm = 'http://azureservices/TenantSite', [Parameter(Mandatory)] [System.Management.Automation.Credential()] [PSCredential] $Credential, [Switch] $ADFS, [Switch] $IgnoreSSL, [Switch] $PassThru ) try { $ErrorActionPreference = 'Stop' if ($ADFS -and $Port -eq 0) { $Port = 443 } elseif ($Port -eq 0 -and $clientRealm -eq 'http://azureservices/TenantSite') { $Port = 30071 } elseif ($Port -eq 0 -and $clientRealm -eq 'http://azureservices/AdminSite') { $Port = 30072 } if ($ADFS) { Write-Verbose -Message 'Constructing ADFS URL' $ConstructedURL = $URL + ":$Port" + '/adfs/services/trust/13/usernamemixed' } else { Write-Verbose -Message 'Constructing ASPNet URL' $ConstructedURL = $URL + ":$Port" + '/wstrust/issue/usernamemixed' } Write-Verbose -Message $ConstructedURL $identityProviderEndpoint = New-Object -TypeName System.ServiceModel.EndpointAddress -ArgumentList $ConstructedURL $identityProviderBinding = New-Object -TypeName System.ServiceModel.WS2007HttpBinding -ArgumentList ([System.ServiceModel.SecurityMode]::TransportWithMessageCredential) $identityProviderBinding.Security.Message.EstablishSecurityContext = $false $identityProviderBinding.Security.Message.ClientCredentialType = 'UserName' $identityProviderBinding.Security.Transport.ClientCredentialType = 'None' $trustChannelFactory = New-Object -TypeName System.ServiceModel.Security.WSTrustChannelFactory -ArgumentList $identityProviderBinding, $identityProviderEndpoint $trustChannelFactory.TrustVersion = [System.ServiceModel.Security.TrustVersion]::WSTrust13 if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL switch defined. Certificate errors will be ignored!' $certificateAuthentication = New-Object -TypeName System.ServiceModel.Security.X509ServiceCertificateAuthentication $certificateAuthentication.CertificateValidationMode = 'None' $trustChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = $certificateAuthentication } if ($ADFS) { $ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($credential.Password) $null = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($ptr) [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($ptr) } $trustChannelFactory.Credentials.SupportInteractive = $false $trustChannelFactory.Credentials.UserName.UserName = $credential.UserName $trustChannelFactory.Credentials.UserName.Password = $credential.GetNetworkCredential().Password $rst = New-Object -TypeName System.IdentityModel.Protocols.WSTrust.RequestSecurityToken -ArgumentList ([System.IdentityModel.Protocols.WSTrust.RequestTypes]::Issue) $rst.AppliesTo = New-Object -TypeName System.IdentityModel.Protocols.WSTrust.EndpointReference -ArgumentList $clientRealm $rst.TokenType = 'urn:ietf:params:oauth:token-type:jwt' $rst.KeyType = [System.IdentityModel.Protocols.WSTrust.KeyTypes]::Bearer $rstr = New-Object -TypeName System.IdentityModel.Protocols.WSTrust.RequestSecurityTokenResponse $channel = $trustChannelFactory.CreateChannel() $token = $channel.Issue($rst, [ref] $rstr) $tokenString = ([System.IdentityModel.Tokens.GenericXmlSecurityToken]$token).TokenXml.InnerText; $token = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($tokenString)) Set-Variable -Name Headers -Scope 1 -Value @{ Authorization = "Bearer $Token" 'x-ms-principal-id' = $Credential.UserName Accept = 'application/json' } Set-Variable -Name Token -Value $token -Scope 1 if ($PassThru) { Write-Output -InputObject $token } } catch { Write-Error -ErrorRecord $_ } } function Connect-WAPAPI { <# .SYNOPSIS Connects to WAPAPI. .PARAMETER Url The URL of either the WAP Public Tenant API or Tenant API. .PARAMETER Port The Port on which the API is listening (default to Public Tenant API port 30006). .PARAMETER IgnoreSSL When using self-signed certificates, SSL validation will be ignored when this switch is enabled. All functions relying on the connection will inherit the SSL setting. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL This will connect to the WAP Public Tenant API on its default port. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL -Port 443 This will connect to the either the WAP Public Tenant API or Tenant API on a non default port 443. #> [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String] $URL, [Int] $Port = 30006, [Switch] $IgnoreSSL ) try { if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL switch defined. Certificate errors will be ignored!' #Change Certificate Policy to ignore IgnoreSSL Set-Variable -Name IgnoreSSL -Value $IgnoreSSL -Scope 1 } PreFlight $TestURL = '{0}:{1}/subscriptions/' -f $URL,$Port Write-Verbose -Message "Constructed Connection URL: $TestURL" $Result = Invoke-WebRequest -Uri $TestURL -Headers $Headers -UseBasicParsing -ErrorVariable 'ErrCon' if ($Result) { Write-Verbose -Message 'Successfully connected' Set-Variable -Name PublicTenantAPIUrl -Value $URL -Scope 1 Set-Variable -Name Port -Value $Port -Scope 1 } else { Write-Verbose -Message 'Connection unsuccessfull' -Verbose Set-Variable -Name PublicTenantAPIUrl -Value $null -Scope 1 Set-Variable -Name Port -Value $null -Scope 1 throw $ErrCon } } catch { Write-Error -ErrorRecord $_ } } function Get-WAPSubscription { <# .SYNOPSIS Retrieves Tenant User Subscription from Azure Pack TenantPublic or Tenant API. .PARAMETER Name The Name of the subscription to be acquired. .PARAMETER Id The Id of the subscription to be acquired. .PARAMETER List A list of all subscriptions the user has access to. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' This will return the subscription with name 'MySubscription' if it exists. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription This will return a list of the users subscriptions. #> [CmdletBinding(DefaultParameterSetName='List')] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory, ParameterSetName='Name')] [ValidateNotNullOrEmpty()] [String] $Name, [Parameter(Mandatory, ParameterSetName='Id')] [ValidateNotNullOrEmpty()] [String] $Id, [Parameter(ParameterSetName='List')] [Switch] $List, [Parameter(ParameterSetName='Current')] [Switch] $Current ) try { if ($Current) { Write-Output -InputObject $Subscription break } if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL defined by Connect-WAPAPI, Certificate errors will be ignored!' #Change Certificate Policy to ignore IgnoreSSL } PreFlight -IncludeConnection $URL = '{0}:{1}/subscriptions/' -f $PublicTenantAPIUrl,$Port Write-Verbose -Message "Constructed Subscription URL: $URL" $Subscriptions = Invoke-RestMethod -Uri $URL -Headers $Headers -Method Get foreach ($S in $Subscriptions) { if ($PSCmdlet.ParameterSetName -eq 'Name' -and $S.SubscriptionName -ne $Name) { continue } if ($PSCmdlet.ParameterSetName -eq 'Id' -and $S.SubscriptionId -ne $Id) { continue } $S.Created = [datetime]$S.Created Add-Member -InputObject $S -MemberType AliasProperty -Name 'Subscription' -Value SubscriptionId $S.PSObject.TypeNames.Insert(0,'WAP.Subscription') Write-Output -InputObject $S } } catch { Write-Error -ErrorRecord $_ } finally { #Change Certificate Policy to the original if ($IgnoreSSL) { [System.Net.ServicePointManager]::CertificatePolicy = $OriginalCertificatePolicy } } } function Select-WAPSubscription { <# .SYNOPSIS Selects User Subscription from Azure Pack TenantPublic or Tenant API. .PARAMETER Subscription The subscription object acquired via Get-WAPSubscription. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription This will select the subscription 'MySubscription'. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNull()] [PSCustomObject] $Subscription ) try { if ($input.count -gt 1) { throw 'Only 1 subscription can be selected. If passed from Get-WAPSubscription, make sure only 1 subscription object is passed on the pipeline' } if (!($Subscription.pstypenames.Contains('WAP.Subscription'))) { throw 'Object bound to Subscription parameter is of the wrong type' } Write-Verbose -Message "Setting current subscription to $($Subscription | Out-String)" Set-Variable -Name Subscription -Value $Subscription -Scope 1 } catch { Write-Error -ErrorRecord $_ } } function Get-WAPGalleryVMRole { <# .SYNOPSIS Retrieves VM Role Gallery Items asigned to Tenant user Subscription from Azure Pack TenantPublic or Tenant API. .PARAMETER List Defaults to list mode. Shows all VM Role Gallery Items. .PARAMETER Name When Name is specified, only the VM Role Gallery Item with the specified name is returned. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>Get-WAPGalleryVMRole This will retrieve all VM Role Gallery Items tight to the subscription. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>Get-WAPGalleryVMRole -Name 'MyAwesomeVMRole' This will retreive only the VM Role Gallery Item with the same name as specified. #> [CmdletBinding(DefaultParameterSetName='List')] [OutputType([PSCustomObject])] param ( [Parameter(ParameterSetName='List')] [Switch] $List, [Parameter(Mandatory, ParameterSetName='Name')] [ValidateNotNullOrEmpty()] [String] $Name ) process { try { if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL defined by Connect-WAPAPI, Certificate errors will be ignored!' #Change Certificate Policy to ignore IgnoreSSL } PreFlight -IncludeConnection -IncludeSubscription $URI = '{0}:{1}/{2}/Gallery/GalleryItems/$/MicrosoftCompute.VMRoleGalleryItem?api-version=2013-03' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId Write-Verbose -Message "Constructed Gallery Item URI: $URI" $GalleryItems = Invoke-RestMethod -Uri $URI -Headers $Headers -Method Get foreach ($G in $GalleryItems.value) { if ($PSCmdlet.ParameterSetName -eq 'Name' -and $G.Name -ne $Name) { continue } $GIResDEFUri = '{0}:{1}/{2}/{3}/?api-version=2013-03' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId,$G.ResourceDefinitionUrl Write-Verbose -Message "Acquiring ResDef from URI: $GIResDEFUri" $ResDef = Invoke-RestMethod -Uri $GIResDEFUri -Headers $Headers -Method Get $GIViewDefUri = '{0}:{1}/{2}/{3}/?api-version=2013-03' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId,$G.ViewDefinitionUrl Write-Verbose -Message "Acquiring ViewDef from URI: $GIResDEFUri" $ViewDef = Invoke-RestMethod -Uri $GIViewDefUri -Headers $Headers -Method Get Add-Member -InputObject $G -MemberType NoteProperty -Name ResDef -Value $ResDef Add-Member -InputObject $G -MemberType NoteProperty -Name ViewDef -Value $ViewDef $G.PublishDate = [datetime]$G.PublishDate $G.PSObject.TypeNames.Insert(0,$G.'odata.type') Write-Output -InputObject $G } } catch { Write-Error -ErrorRecord $_ } finally { #Change Certificate Policy to the original if ($IgnoreSSL) { [System.Net.ServicePointManager]::CertificatePolicy = $OriginalCertificatePolicy } } } } function Get-WAPVMRoleOSDisk { <# .SYNOPSIS Retrieves Available VMRole OS Disks based on Gallery Item from Azure Pack TenantPublic or Tenant API. .PARAMETER ViewDef The viewdef comes as a property of the VM Role gallery item. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>$GI = Get-WAPGalleryVMRole -Name MyVMRole PS C:\>$GI | Get-WAPVMRoleOSDisk -Verbose This will fetch all compatible and enabled OS disks. #> [CmdletBinding()] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNull()] [PSCustomObject] $ViewDef ) process { try { if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL defined by Connect-WAPAPI, Certificate errors will be ignored!' #Change Certificate Policy to ignore IgnoreSSL } PreFlight -IncludeConnection -IncludeSubscription $URI = '{0}:{1}/{2}/services/systemcenter/vmm/VirtualHardDisks' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId Write-Verbose -Message "Constructed VHD URI: $URI" $Sections = $ViewDef.ViewDefinition.Sections $Categories = $Sections | ForEach-Object -Process {$_.Categories} $OSDiskParam = $Categories | ForEach-Object -Process {$_.Parameters} | Where-Object -FilterScript {$_.Type -eq 'OSVirtualHardDisk'} $Images = Invoke-RestMethod -Uri $URI -Headers $Headers -Method Get foreach ($I in $Images.value) { $Tags = $I.tag if ($null -eq (Compare-Object -ReferenceObject $Tags -DifferenceObject $OSDiskParam.ImageTags).SideIndicator) { if ($I.enabled -eq $false) { continue } $I.AddedTime = [datetime] $I.AddedTime $I.ModifiedTime = [datetime] $I.ModifiedTime $I.ReleaseTime = [datetime] $I.ReleaseTime $I.PSObject.TypeNames.Insert(0,'WAP.GI.OSDisk') Write-Output -InputObject $I } } } catch { Write-Error -ErrorRecord $_ } finally { #Change Certificate Policy to the original if ($IgnoreSSL) { [System.Net.ServicePointManager]::CertificatePolicy = $OriginalCertificatePolicy } } } } function Get-WAPVMNetwork { <# .SYNOPSIS Retrieves subscription available VM Networks from Azure Pack TenantPublic or Tenant API. .PARAMETER List Defaults to list mode. Shows all VM Networks available to the subscription. .PARAMETER Name When Name is specified, only the VM Network with the specified name is returned. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>Get-WAPVMNetwork This will fetch all VM Networks available to the subscription. #> [CmdletBinding(DefaultParameterSetName='List')] [OutputType([PSCustomObject])] param ( [Parameter(ParameterSetName='List')] [Switch] $List, [Parameter(Mandatory, ParameterSetName='Name')] [ValidateNotNullOrEmpty()] [String] $Name, [Switch] $IgnoreSSL ) process { try { if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL defined by Connect-WAPAPI, Certificate errors will be ignored!' #Change Certificate Policy to ignore IgnoreSSL } PreFlight -IncludeConnection -IncludeSubscription $URI = '{0}:{1}/{2}/services/systemcenter/vmm/VMNetworks' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId Write-Verbose -Message "Constructed VM Networks URI: $URI" $VMNets = Invoke-RestMethod -Uri $URI -Headers $Headers -Method Get foreach ($N in $VMNets.value) { if ($PSCmdlet.ParameterSetName -eq 'Name' -and $N.Name -ne $Name) { continue } $N.PSObject.TypeNames.Insert(0,'WAP.VMNetwork') Write-Output -InputObject $N } } catch { Write-Error -ErrorRecord $_ } finally { #Change Certificate Policy to the original if ($IgnoreSSL) { [System.Net.ServicePointManager]::CertificatePolicy = $OriginalCertificatePolicy } } } } function New-WAPVMRoleParameterObject { <# .SYNOPSIS Generates VM Role Parameter Object. .PARAMETER VMRole VM Role gallery item object acquired via Get-WAPGalleryVMRole .PARAMETER OSDisk OS Disk object acquired via Get-WAPVMRoleOSDisk .PARAMETER VMRoleVMSize Select one of the default VMRole sizing profiles ('Small','A7','ExtraSmall','Large','A6','Medium','ExtraLarge') .PARAMETER VMNetwork VM Network object acquired via Get-WAPVMNetwork .PARAMETER Interactive Run in interactive mode where you get prompted to provide values with parameters. In non-interactive mode this functions uses the defaults where provided and uses NULL for everything unknown. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>$GI = Get-WAPGalleryVMRole -Name MyVMRole PS C:\>$OSDisk = $GI | Get-WAPVMRoleOSDisk | Sort-Object -Property AddedTime -Descending | Select-Object -First 1 PS C:\>$NW = Get-WAPVMNetwork -Name MyNetwork PS C:\>$VMProps = New-WAPVMRoleParameterObject -VMRole $GI -OSDisk $OSDisk -VMRoleVMSize Large -VMNetwork $NW -Interactive This will run in interactive mode. It will prompt to fill in the blanks and accept defaults or provide own values. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>$GI = Get-WAPGalleryVMRole -Name MyVMRole PS C:\>$OSDisk = $GI | Get-WAPVMRoleOSDisk | Sort-Object -Property AddedTime -Descending | Select-Object -First 1 PS C:\>$NW = Get-WAPVMNetwork -Name MyNetwork PS C:\>$VMProps = New-WAPVMRoleParameterObject -VMRole $GI -OSDisk $OSDisk -VMRoleVMSize Large -VMNetwork $NW PS C:\>$VMProps.MissingValue = 'MyValue' This will run in non-interactive mode. It will use defaults and assigns NULL if no default is available. Values can be assigned / overwritten. #> [CmdletBinding(SupportsShouldProcess=$true)] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory)] [ValidateNotNull()] [PSCustomObject] $VMRole, [Parameter(Mandatory)] [ValidateNotNull()] [PSCustomObject] $OSDisk, [Parameter(Mandatory)] [ValidateSet('Small','A7','ExtraSmall','Large','A6','Medium','ExtraLarge')] [String] $VMRoleVMSize, [Parameter(Mandatory)] [ValidateNotNull()] [PSCustomObject] $VMNetwork, [Switch] $Interactive ) if (!($VMRole.pstypenames.Contains('MicrosoftCompute.VMRoleGalleryItem'))) { throw 'Object bound to VMRole parameter is of the wrong type' } if (!($OSDisk.pstypenames.Contains('WAP.GI.OSDisk'))) { throw 'Object bound to OSDisk parameter is of the wrong type' } if (!($VMNetwork.pstypenames.Contains('WAP.VMNetwork'))) { throw 'Object bound to VMNetwork parameter is of the wrong type' } if ($PSCmdlet.ShouldProcess($null,'Generating new ParameterObject')) { $Sections = $VMRole.ViewDef.ViewDefinition.Sections $Categories = $Sections | ForEach-Object -Process {$_.Categories} $ViewDefParams = $Categories | ForEach-Object -Process {$_.Parameters} $Output = [pscustomobject]@{} foreach ($P in $ViewDefParams) { $p | Out-String | Write-Verbose if ($Interactive -and $P.type -eq 'option') { $values = '' foreach ($v in $P.OptionValues) { $Def = ($v | Get-Member -MemberType NoteProperty).Definition.Split(' ')[1].Split('=') #$Friendly = $Def[1] $Value = $Def[0] $values += $value + ',' } $values = $values.TrimEnd(',') if ($P.DefaultValue) { if(($result = Read-Host -Prompt "Press enter to accept default value $($P.DefaultValue) for $($P.Name). Valid entries: $values") -eq ''){ Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value $P.DefaultValue -Force } else { do { $result = Read-Host -Prompt "Enter one of the following entries: $values" } while (@($values.Split(',')) -notcontains $result) Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value $result -Force } } else { do { $result = Read-Host -Prompt "Enter one of the following entries: $values" } while (@($values.Split(',')) -notcontains $result) Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value $result -Force } } elseif ($Interactive -and $P.type -eq 'Credential') { do { $result = Read-Host -Prompt "Enter a credential for $($P.Name) in the format domain\username:password or username:password" } while ($result -notmatch '\w+\\+\w+:+\w+' -and $result -notmatch '\w+:+\w+') Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value $result -Force } elseif ($P.DefaultValue) { Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value $P.DefaultValue -Force } elseif ($P.Type -eq 'OSVirtualHardDisk') { Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value "$($OSDisk.FamilyName):$($OSDisk.Release)" -Force } elseif ($P.Type -eq 'VMSize') { Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value $VMRoleVMSize -Force } elseif ($P.Type -eq 'Credential') { Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value 'domain\username:password' -Force } elseif ($P.Type -eq 'Network') { Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value $($VMNetwork.Name) -Force } elseif ($Interactive) { $result = Read-Host -Prompt "Enter a value for $($P.Name) of type $($P.Type)" Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value $result -Force } else { Add-Member -InputObject $Output -MemberType NoteProperty -Name $P.Name -Value $null -Force } } $Output.PSObject.TypeNames.Insert(0,'WAP.ParameterObject') Write-Output -InputObject $Output } } function Get-WAPCloudService { <# .SYNOPSIS Retrieves Cloudservice deployed to subscription from Azure Pack TenantPublic or Tenant API. .PARAMETER List Defaults to list mode. Shows all cloud services provisioned for the subscription. .PARAMETER Name When Name is specified, only the cloud service with the specified name is returned. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>Get-WAPCloudService This will retreive all provisioned cloud services for the specified subscription. #> [CmdletBinding(DefaultParameterSetName = 'List')] [OutputType([PSCustomObject])] param ( [Parameter(ParameterSetName = 'List')] [Switch] $List, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Alias('CloudServiceName')] [ValidateNotNullOrEmpty()] [String] $Name ) process { try { if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL defined by Connect-WAPAPI, Certificate errors will be ignored!' #Change Certificate Policy to ignore IgnoreSSL } PreFlight -IncludeConnection -IncludeSubscription $URI = '{0}:{1}/{2}/CloudServices?api-version=2013-03' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId Write-Verbose -Message "Constructed CloudService URI: $URI" $CloudServices = Invoke-RestMethod -Uri $URI -Headers $Headers -Method Get foreach ($C in $CloudServices.value) { if ($PSCmdlet.ParameterSetName -eq 'Name' -and $C.Name -ne $Name) { continue } Add-Member -InputObject $C -MemberType AliasProperty -Name CloudServiceName -Value Name $C.PSObject.TypeNames.Insert(0,'WAP.CloudService') Write-Output -InputObject $C } } catch { Write-Error -ErrorRecord $_ } finally { #Change Certificate Policy to the original if ($IgnoreSSL) { [System.Net.ServicePointManager]::CertificatePolicy = $OriginalCertificatePolicy } } } } function New-WAPCloudService { <# .SYNOPSIS Creates Cloudservice for subscription from Azure Pack TenantPublic or Tenant API. .PARAMETER Name The name of the cloud service to be provisioned. The name must be unique within the subscription. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>New-WAPCloudService -Name test This will provision a cloud service named test. #> [CmdletBinding(SupportsShouldProcess=$true)] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [Alias('CloudServiceName')] [ValidateNotNullOrEmpty()] [String] $Name ) process { try { if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL defined by Connect-WAPAPI, Certificate errors will be ignored!' #Change Certificate Policy to ignore IgnoreSSL } PreFlight -IncludeConnection -IncludeSubscription if ($PSCmdlet.ShouldProcess($Name)) { $URI = '{0}:{1}/{2}/CloudServices?api-version=2013-03' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId Write-Verbose -Message "Constructed CloudService URI: $URI" $CloudServiceConfig = @{ Name = $Name Label = $Name } | ConvertTo-Json -Compress $CloudService = Invoke-RestMethod -Uri $URI -Headers $Headers -Method Post -Body $CloudServiceConfig -ContentType 'application/json' $CloudService.PSObject.Properties.Remove('odata.metadata') $CloudService.PSObject.TypeNames.Insert(0,'WAP.CloudService') Write-Output -InputObject $CloudService } } catch { Write-Error -ErrorRecord $_ } finally { #Change Certificate Policy to the original if ($IgnoreSSL) { [System.Net.ServicePointManager]::CertificatePolicy = $OriginalCertificatePolicy } } } } function Remove-WAPCloudService { <# .SYNOPSIS Deletes Cloudservice from subscription from Azure Pack TenantPublic or Tenant API. .PARAMETER Name The name of the cloud service to be removed. .PARAMETER Force If Force is not specified, removal is treated with confirm impact high. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>Remove-WAPCloudService -Name test This will remove the cloudservice named test from the subscription. If a VM Role has been deployed to this cloud service, it will be removed as well. In this case, the user will be prompted to confirm the remove action as -Force or -Confirm:$false is not specified. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>Get-WAPCloudService -Name Test | Remove-WAPCloudService -Force This will remove the cloudservice named test from the subscription. If a VM Role has been deployed to this cloud service, it will be removed as well. In this case, the user is not prompted to confirm as -Force is specified. #> [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [String] $Name, [Switch] $Force ) process { try { if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL defined by Connect-WAPAPI, Certificate errors will be ignored!' #Change Certificate Policy to ignore IgnoreSSL } PreFlight -IncludeConnection -IncludeSubscription $URI = '{0}:{1}/{2}/CloudServices?api-version=2013-03' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId Write-Verbose -Message "Constructed CloudService URI: $URI" $CloudServices = Invoke-RestMethod -Uri $URI -Method Get -Headers $Headers foreach ($C in $CloudServices.value) { if ($C.Name -ne $Name) { continue } $RemURI = '{0}:{1}/{2}/CloudServices/{3}?api-version=2013-03' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId,$Name Write-Verbose -Message "Constructed Named CloudService URI: $RemURI" if ($Force -or $PSCmdlet.ShouldProcess($Name)) { Invoke-RestMethod -Uri $RemURI -Method Delete -Headers $Headers | Out-Null } } } catch { Write-Error -ErrorRecord $_ } finally { #Change Certificate Policy to the original if ($IgnoreSSL) { [System.Net.ServicePointManager]::CertificatePolicy = $OriginalCertificatePolicy } } } } function New-WAPVMRoleDeployment { <# .SYNOPSIS Deploys VM Role to a Cloudservice using Azure Pack TenantPublic or Tenant API. .PARAMETER CloudServiceName The name of the cloud service to provision to. If it does not exist, it will be created. .PARAMETER VMRole Object acquired with Get-WAPGalleryVMRole. .PARAMETER ParameterObject Object acquired with New-WAPVMRoleParameterObject. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>$GI = Get-WAPGalleryVMRole -Name DomainController PS C:\>$OSDisk = $GI | Get-WAPVMRoleOSDisk | Sort-Object -Property AddedTime -Descending | Select-Object -First 1 PS C:\>$NW = Get-WAPVMNetwork -Name Private PS C:\>$VMProps = New-WAPVMRoleParameterObject -VMRole $GI -OSDisk $OSDisk -VMRoleVMSize Large -VMNetwork $NW PS C:\>$VMProps.DomainName = 'MyNewDomain.local' PS C:\>New-WAPVMRoleDeployment -VMRole $GI -ParameterObject $VMProps -CloudServiceName DCs -Verbose This will deploy a new VM Role based on the Gallery Item DomainController. It will link the VMs up to the Private network and uses the latest published OS Disk. The domain name for the VM Role will be 'MyNewDomain.local' and the VMs will be sided using the Large VM Profile. If the cloud service DCs does not yet exists, it will be created. If it does exist, it will be checked if it has the correct name and if no VM Roles have been deployed to it. This function mirrors portal functionality and therefore does not allow multiple VM Roles in one cloud service. #> [CmdletBinding(SupportsShouldProcess=$true)] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory)] [ValidateNotNull()] [PSCustomObject] $VMRole, [Parameter(Mandatory)] [ValidateNotNull()] [PSCustomObject] $ParameterObject, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [Alias('Name','VMRoleName')] [ValidateNotNullOrEmpty()] [String] $CloudServiceName ) process { $ErrorActionPreference = 'Stop' if (!($VMRole.pstypenames.Contains('MicrosoftCompute.VMRoleGalleryItem'))) { throw 'Object bound to VMRole parameter is of the wrong type' } if (!($ParameterObject.pstypenames.Contains('WAP.ParameterObject'))) { throw 'Object bound to ParameterObject parameter is of the wrong type' } $ParameterObject | Get-Member -MemberType Properties | ForEach-Object -Process { if ($null -eq $ParameterObject.($_.name)) { throw "ParameterObject property: $($_.name) is NULL" } } try { if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL defined by Connect-WAPAPI, Certificate errors will be ignored!' #Change Certificate Policy to ignore IgnoreSSL } PreFlight -IncludeConnection -IncludeSubscription if ($PSCmdlet.ShouldProcess($CloudServiceName)) { Write-Verbose -Message "Testing if Cloudservice $CloudServiceName exists" if (!(Get-WAPCloudService -Name $CloudServiceName)) { Write-Verbose -Message "Creating Cloudservice $CloudServiceName as it does not yet exist" New-WAPCloudService -Name $CloudServiceName | Out-Null $New = $true } else { $New = $false } if (!$New) { Write-Verbose -Message "Testing if VMRole does not already exist within cloud service" if (Get-WAPCloudService -Name $CloudServiceName | Get-WAPVMRole) { throw "There is already a VMRole deployed to the CloudService $CloudServiceName. Because this function mimics portal experience, only one VM Role is allowed to exist per CloudService" } } #Add ResDefConfig JSON to Dictionary $ResDefConfig = New-Object -TypeName 'System.Collections.Generic.Dictionary[String,Object]' $ResDefConfig.Add('Version',$VMRole.version) $ResDefConfig.Add('ParameterValues',($ParameterObject | ConvertTo-Json)) # Set Gallery Item Payload Info $GIPayload = @{ InstanceView = $null Substate = $null Name = $CloudServiceName Label = $CloudServiceName ProvisioningState = $null ResourceConfiguration = $ResDefConfig ResourceDefinition = $VMRole.ResDef } # Convert Gallery Item Payload Info To JSON $GIPayloadJSON = ConvertTo-Json -InputObject $GIPayload -Depth 10 # Deploy VM Role to cloudservice $URI = '{0}:{1}/{2}/CloudServices/{3}/Resources/MicrosoftCompute/VMRoles/?api-version=2013-03' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId,$CloudServiceName Write-Verbose -Message "Constructed VMRole Deploy URI: $URI" Write-Verbose -Message "Starting deployment of VMRole $VMRoleName to CloudService $CloudServiceName" $Deploy = Invoke-RestMethod -Uri $URI -Headers $Headers -Method Post -Body $GIPayloadJSON -ContentType 'application/json' $Deploy.PSObject.TypeNames.Insert(0,'WAP.VMRole') Write-Output -InputObject $Deploy } } catch { if ($New) { Get-WAPCloudService -Name $CloudServiceName | Remove-WAPCloudService -Force } Write-Error -ErrorRecord $_ } finally { #Change Certificate Policy to the original if ($IgnoreSSL) { [System.Net.ServicePointManager]::CertificatePolicy = $OriginalCertificatePolicy } } } } function Get-WAPVMRole { <# .SYNOPSIS Retrieves Deployed VM Role information from Azure Pack TenantPublic or Tenant API. .PARAMETER CloudServiceName The name of the cloud service to get VM Role information from. .EXAMPLE PS C:\>$URL = 'https://publictenantapi.mydomain.com' PS C:\>$creds = Get-Credential PS C:\>Get-WAPToken -Credential $creds -URL 'https://sts.adfs.com' -ADFS PS C:\>Connect-WAPAPI -URL $URL PS C:\>Get-WAPSubscription -Name 'MySubscription' | Select-WAPSubscription PS C:\>Get-WAPCloudService -Name DCs | Get-WAPVMRole | select * This will get the VM Role provisioning information for the DCs cloud service deployment. #> [CmdletBinding()] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [Alias('Name','VMRoleName')] [ValidateNotNullOrEmpty()] [String] $CloudServiceName ) process { try { if ($IgnoreSSL) { Write-Warning -Message 'IgnoreSSL defined by Connect-WAPAPI, Certificate errors will be ignored!' #Change Certificate Policy to ignore IgnoreSSL } PreFlight -IncludeConnection -IncludeSubscription $URI = '{0}:{1}/{2}/CloudServices/{3}/Resources/MicrosoftCompute/VMRoles?api-version=2013-03' -f $PublicTenantAPIUrl,$Port,$Subscription.SubscriptionId,$CloudServiceName Write-Verbose -Message "Constructed VMRole URI: $URI" $Roles = Invoke-RestMethod -Uri $URI -Headers $Headers -Method Get foreach ($R in $Roles.value) { Add-Member -InputObject $R -MemberType NoteProperty -Name ParameterValues -Value ($R.ResourceConfiguration.ParameterValues | ConvertFrom-Json) Add-Member -InputObject $R -MemberType NoteProperty -Name ScaleOutSettings -Value $R.ResourceDefinition.IntrinsicSettings.ScaleOutSettings Add-Member -InputObject $R -MemberType NoteProperty -Name InstanceCount -Value $R.InstanceView.InstanceCount Add-Member -InputObject $R -MemberType NoteProperty -Name VMSize -Value $R.InstanceView.ResolvedResourceDefinition.IntrinsicSettings.HardwareProfile.VMSize $R.PSObject.TypeNames.Insert(0,'WAP.VMRole') Write-Output -InputObject $R } } catch { Write-Error -ErrorRecord $_ } finally { #Change Certificate Policy to the original if ($IgnoreSSL) { [System.Net.ServicePointManager]::CertificatePolicy = $OriginalCertificatePolicy } } } } Export-ModuleMember -Function *-WAP* Export-ModuleMember -Variable Token,Headers,PublicTenantAPIUrl,Port,IgnoreSSL,Subscription |