Functions/New-CmEC2Instance.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
Function New-CmEC2Instance           {
    <#
.Synopsis
    Creates a Windows EC2 On demand or spot Instance with minimal input.
.DESCRIPTION
    Creates a Windows EC2 On demand or spot Instance and, if a Name and DomainName are specified, creates or updates the Route 53 DNS entry for the instance and applies a Name Tag.
 
.NOTES
    Name: New-CMEC2Instance
    Author: Chad Miles
    DateUpdated: 2017-05-02
    Version: 1.2.0
 
.EXAMPLE
   C:\> New-CMEC2Instance -InstanceType t2.micro -Region us-east-1 -DomainName mydomain.com -Name MyInstance
    
   InstanceID : i-1234567890abcdef
   Region : us-east-1
   Name : MyInstance
   Hostname : MyInstance.Mydomain.com
   InstanceType : t2-micro
   BidPrice :
   OnDemandPrice : 0.017
   Savings : 0 %
   ImageName : WINDOWS_2016_BASE
   ImageID : ami-58a1a73e
   KeyName : MyKeyPair
    
.EXAMPLE
   C:\> New-CMEC2Instance -InstanceType t2.micro -Region us-east-1 -Name MyInstance
    
   InstanceID : i-1234567890abcdef
   Region : us-east-1
   Name : MyInstance
   Hostname : ec2-34-248-2-178.eu-west-1.compute.amazonaws.com
   InstanceType : t2-micro
   BidPrice :
   OnDemandPrice : 0.017
   Savings : 0 %
   ImageName : WINDOWS_2016_BASE
   ImageID : ami-58a1a73e
   KeyName : MyKeyPair
 
.EXAMPLE
   C:\> New-CMEC2Instance -InstanceType t2.micro -Region us-east-1 -Name MyInstance -DomainName mydomain.com -OSVerion 2012R2
    
   InstanceID : i-1234567890abcdef
   Region : us-east-1
   Name : MyInstance
   Hostname : MyInstance.Mydomain.com
   InstanceType : t2-micro
   BidPrice :
   OnDemandPrice : 0.017
   Savings : 0 %
   ImageName : WINDOWS_2012R2_BASE
   ImageID : ami-40003a26
   KeyName : MyKeyPair
 
.EXAMPLE
   C:\> New-CMEC2Instance -InstanceType t2.micro -Region us-east-1 -Name MyInstance -DomainName mydomain.com -SpotRequest
   WARNING: Spot Instances not available for T1 and T2 instance types, switching to on demand.
 
   InstanceID : i-1234567890abcdef
   Region : us-east-1
   Name : MyInstance
   Hostname : MyInstance.Mydomain.com
   InstanceType : t2-micro
   BidPrice :
   OnDemandPrice : 0.017
   Savings : 0 %
   ImageName : WINDOWS_2016_BASE
   ImageID : ami-40003a26
   KeyName : MyKeyPair
 
.EXAMPLE
   C:\> New-CMEC2Instance -InstanceType m3.medium -Region us-east-1 -Name MySpotInstance -DomainName mydomain.com -SpotRequest
    
   InstanceID : i-1234567890abcdef
   Region : us-east-1
   Name : MySpotInstance
   Hostname : MySpotInstance.Mydomain.com
   InstanceType : m3.medium
   BidPrice : 0.0741
   OnDemandPrice : 0.13
   Savings : 55 %
   ImageName : WINDOWS_2016_BASE
   ImageID : ami-58a1a73e
   KeyName : MyKeyPair
 
#>

    [CmdletBinding(SupportsShouldProcess=$true,
                   DefaultParameterSetName='SearchBaseIds')]
    Param (
        [ValidateScript({@((Get-AWSRegion).Region)})]
        [string] $Region,
        [Parameter(Mandatory=$true)]
        [ValidateScript({(Get-CmEc2InstanceTypes)})]
        [String] $InstanceType,
        # Applies this name tag to the instance after creation, if -DomainName is specified as well then registers a DNS CNAME for your instance using this name
        [string] $Name,
        # If -Name is also specified then a DNS CNAME is registered in this domain, provided the domain is hosted in R53 and you have rights to do so.
        [string] $DomainName,
        # Version of Windows e.g. 2012R2 or 2016. Default is 2012R2
        [ValidateSet("WindowsServer2019",
            "WindowsServer1809", 
            "WindowsServer1803",
            "WindowsServer1709",
            "WindowsServer2016",
            "WindowsServer2012R2",
            "WindowsServer2012",
            "WindowsServer2008R2",
            "WindowsServer2008",
            "WindowsServer2003",
            "2019",
            "1809",
            "1803",
            "1709",
            "2016",
            "2012R2",
            "2012",
            "2008R2",
            "2008",
            "2003",
            "AmazonLinux",
            "Ubuntu18.04",
            "Ubuntu16.04",
            "AmazonLinux2")]
        [string] $OsVersion = 2016,

        [Parameter(ParameterSetName='SearchBaseIds')]
        [switch] $Base=$true,

        [Parameter(ParameterSetName='SearchSqlIds')]
        [ValidateSet("2017","2016","2014","2012","2008R2","2008","2005")]
        [string] $SqlVersion,

        [Parameter(ParameterSetName='SearchSqlIds')]
        [ValidateSet("Express", "Web","Standard","Enterprise")]
        [string] $SqlEdition = "Standard",

        [switch] $Core,

        # Instance Profile (with IAM Role) to attach to new Instance
        [string] $InstanceProfile,
        # Path to User data file , using Your My Documents folder as a root
        [string] $UserData,
        # What Percrentage to add to the lowest Spot Price to ensure instance's longevity
        [int]    $MarkUp     = 1,
        #Specify an AMI id like ami-2b8c8452
        [Parameter(ValueFromPipeline       =$true,
            ValueFromPipelineByPropertyName=$true,
            ParameterSetName               ='ImageId',
            Mandatory                      =$true)]
        [string] $ImageId,
        # The name of the Security Group, not the Security Group ID. The Function will get the ID. If none is specified the default Security Group is used.
        [string] $SecurityGroupName,
        # Switch to specify that a Spot instance should be created instead of On Demand. Bid price will be automatically calculated based on current bid price plus a markup percentage of which the default is 1.
        [switch] $SpotInstance,
        # The Name of the EC2 KeyPair to use when creating the instance
        [string] $KeyName,
        # The ID of the VPC you wish to use, if not specified, the default one is used.
        [string] $VpcId,
        # Last letter of the Availability Zone like a, b, c, d or e, if none specified, a is used
        [string] $AZSuffix,
        [string] $SubNetId,
        [ValidateRange(1,15)]
        [Int]    $NetworkInerfaces = 1

    )
    <#DynamicParam
    {
        $Names = Get-CmEc2InstanceTypes
        $ParameterName = 'InstanceType'
        $ParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $ParamAttrib = New-Object System.Management.Automation.ParameterAttribute
        $ParamAttrib.Mandatory= 1
        $AttribColl = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        $AttribColl.Add($ParamAttrib)
        $AttribColl.Add((New-Object System.Management.Automation.ValidateSetAttribute($Names)))
        $RuntimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttribColl)
        $RuntimeParamDic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $RuntimeParamDic.Add($ParameterName, $RuntimeParam)
        return $RuntimeParamDic
    }#>

    BEGIN 
    {
        $ErrorActionPreference    = "Stop"
        if ($AZSuffix){
            if (-not $Region) {$Region = (Get-DefaultAWSRegion).Region}
            if (-not $Region) {try {$Region = (Get-EC2InstanceMetadata -Category Region).SystemName} catch {}}
            $AvailabilityZone      = $Region+$AZSuffix
        }
        
    }
    Process {
        if ($SqlVersion) 
        {
            Clear-Variable Base
            if ($InstanceType -like "t2.*" -and $SqlEdition -ne "Express") {Write-Error "Non Express editions of SQL Server are not permited to run on t2 instance types"}
        }
        [int]$Count               = 1 
        
        Write-Verbose          "Geting On Demand Instance Pricing"
        $OndemandPrice         = Get-EC2WindowsOndemandPrice -InstanceType $InstanceType -Region $Region
        
        if (!$ImageID) 
        {
            Write-Verbose       "Getting Current AMI for Selected OS"
            $ImageParam      = @{OsVersion = $OsVersion}
            If ($Core)       {$ImageParam.Add('Core',$true)}
            If ($Region)     {$ImageParam.Add('Region',$Region)}
            If ($SqlVersion) {$ImageParam.Add('SqlVersion',$SqlVersion)}
            If ($SqlEdition) {$ImageParam.Add('SqlEdition',$SqlEdition)}
            $ImageID           = Get-CMEC2ImageId @ImageParam
        }
        if (!$ImageID) {         Write-Error "Could not find an image with Search criteria in region $Region"}
        if (!$Keyname) 
        {
            Write-Verbose       "Getting first KeyPair for Region"
            $KeyName           = (Get-EC2KeyPair -Region $Region)[0].KeyName
        }
        if (!$KeyName)          {Write-Error "No EC2 Key Pairs found in region $Region, please create one"}
        If ($UserData)          {$UserData64 = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($UserData))}
        If (!$SubNetId){ 
            If (!$VpcId)   
            {
                Write-Verbose       "Getting default VPC"
                $VpcId             = (Get-EC2Vpc -Region $Region| Where {$_.IsDefault -eq $true}).VpcId
            }
            If (!$VpcId)   {         Write-Error "Could not find default VPC in region $Region, Please specify either a VPC or a Subnet Id"}
            Write-Verbose       "Getting Subnet for name $AvailabilityZone"
            $SubNets = Get-EC2Subnet -Region $Region -Filter @{Name='vpc-id';Values=$VpcId}
            If ($AvailabilityZone) {$SubNetId = ( $SubNets | where {$_.AvailabilityZone -eq $AvailabilityZone})[0].SubnetId}
            else {$SubNetId = ($SubNets | where {$_.VpcId -eq $VPCid})[(Get-Random -Maximum $($SubNets.Count-1))].SubnetId}
        } Else {
            $VpcId = (Get-EC2Subnet -Region $Region -SubnetId $SubNetId).VpcId
        }
        If (!$VpcId) {Write-Error "Could not determine VPC, check you have a default VPC in the region and if SubnetId is specified, make sure it is valid"}
    
        If ($SecurityGroupName)  {
            Write-Verbose       "Getting Security Group for name $SecurityGroupName"
            $SecurityGroupId   = (Get-EC2SecurityGroup -Region $Region | where {$_.GroupName -eq $SecurityGroupName -and $_.VpcId -eq $VpcId})[0].GroupId
            If (!$SecurityGroupId) {Write-Error "Security Group with $SecurityGroupName cannot be found"}
        } 
        else {
            Write-Verbose       "Getting Security Group for VPC $VpcId"
            $SecurityGroupId   = (Get-EC2SecurityGroup -Region $Region | where {$_.GroupName -eq "default" -and $_.VpcId -eq $VPCId})[0].GroupId
        }
        If (!$SecurityGroupId)  {
            Write-Error "Could not find a Security Group with the name $SecurityGroupName in region $Region"
        }
    
        $Params            = @{
            Region               = $Region
            MinCount             = $Count
            MaxCount             = $Count
            InstanceType         = $InstanceType
            SecurityGroupId      = $SecurityGroupId
            ImageId              = $ImageId
            SubnetId             = $SubNetId
            KeyName              = $KeyName
        }
            
        if ($Name) {
            $tag1 = @{ Key="Name"; Value=$Name }
            $Tags = @()
            $tagspec1 = New-Object -Type Amazon.EC2.Model.TagSpecification
            $tagspec1.ResourceType = "instance"
            $tagspec1.Tags.Add($tag1)
            $Tags+=$tagspec1
            $tagspec2 = New-Object -Type Amazon.EC2.Model.TagSpecification
            $tagspec2.ResourceType = "volume"
            $tagspec2.Tags.Add($tag1)
            $Tags+=$tagspec2
            $Params.Add("TagSpecification",$Tags)
        }
        If ($SpotInstance) {
            $InstanceMarketOption = @{
                MarketType ="Spot"
                <#SpotOptions = @{
                    InstanceInterruptionBehavior = "stop"
                    SpotInstanceType = "persistent"
                }#>

            }
            $Params.Add("InstanceMarketOption",$InstanceMarketOption)
        }
            
            If ($UserData64)      {$Params.Add("UserData",$UserData64)}
            If ($InstanceProfile) {$Params.Add("InstanceProfile_Name",$InstanceProfile)}
            $InstanceId       = (New-EC2Instance @Params).Instances.InstanceId
            
        if ($Name) {
            If ($DomainName) {
                $DNSParams        = @{InstanceId    = $InstanceId}
                if($Region)        {$DNSParams.Add('Region',$Region)}
                $SetDns           = Set-CmEc2DnsName @DNSParams -DomainName $DomainName -InstanceName $Name
                $HostName         = $SetDns.Hostname
            }
        } else {
            $HostName = $RunningInstance.PublicIPAddress
        }
        $OutputProperties = @{
            InstanceId            = $InstanceId
            Region                = $Region
            Name                  = $Name
            Hostname              = $HostName
            InstanceType          = $InstanceType
            BidPrice              = $BidPrice
            OnDemandPrice         = $OnDemandPrice
            Savings               = "$Savings %"
            ImageName             = $ImageName
            ImageId               = $ImageId
            KeyName               = $KeyName
            InstanceProfile       = $InstanceProfile
        }
        New-Object -TypeName PSObject -Property $OutputProperties
    }
}