Functions/New-AwsEasyStack.ps1
<# .SYNOPSIS Create full stack from single command. .DESCRIPTION N/A. .EXAMPLE NOTES: atm is for port 80 ingress (world) to server instance port XXXXX... no 443 yet due cert restictions $userdata = '{ $env:computername }' # we need this for aditional ec2 config $policyjson = '{ }' # put your verified json here New-AwsEasyStack -serverclass mystackname -url mydns.something.com -hostedzonename IDvaluesomething -vpcfilter myvpcnameORid -region Where-Object? -tagkey owner -tagvalue you! #> function New-AwsEasyStack { [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="Low")] param( [Parameter(Mandatory=$true)] [string]$serverclass, # name your build [string]$url, # r53 dns desired record [string]$amifilter = "WINDOWS_2016_BASE", # make sure u know the naming here [string]$amiownerid = "self", [string]$instancetype = "t2.micro", [string]$hostedzonename, # used for: $url.$hostedzonename dnsrecord [Parameter(Mandatory=$true)] [string]$vpcfilter, # use vpc name or vpcid [string]$instanceport = "80", # port that elb will redirect and allow in ec2 from [string]$worldport = "80", # any port for elb external access [string[]]$subnetsids, # if you know them use them [Parameter(Mandatory=$true)] [string]$tagkey, [Parameter(Mandatory=$true)] [string]$tagvalue, [string]$userdata, [Parameter(Mandatory=$true)] [string]$region, [string]$policyjson ) process{ # TODO verify json integrity for policy # LOGS if(!(test-path c:\powershell-logs)){ new-item -path c:\powershell-logs -itemtype directory } $logfile = 'c:\powershell-logs\output-log-CreateAStackFromPS.txt' Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) ------------------- Started process for stack $serverclass." | out-file -append -encoding ascii $logfile # REGION $getregion = Get-EC2InstanceMetadata -Category Region | Select-Object -ExpandProperty SystemName if($null -ne $getregion){ $region = $getregion } Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) You are in region $region." | out-file -append -encoding ascii $logfile # VPC $vpcs = Get-EC2Vpc -Region $region $getvpcs = foreach($vpc in $vpcs){ foreach($t in $vpc.tags){ $vpc | Add-Member -MemberType NoteProperty -Name $t.Key -Value $t.Value -ErrorAction SilentlyContinue -Force } $vpc | Select-Object * } $vpc = $getvpcs | Where-Object {$_.Name -like "*$vpcfilter*" -or $_.VpcId -eq $vpcfilter} $vpc if($($vpc.count) -gt '1'){ Write-Warning "Found $($vpc.count) VPCs." break } if($null -eq $vpc){ Write-Warning "No VPC found." break } Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) Identified vpc $($vpc.VpcId)." | out-file -append -encoding ascii $logfile # continue Write-Warning "Region set to $region" $serverclass_check = $serverclass.Length if($serverclass_check -gt '28'){ # 28 ensure all other commands fill requirements Write-Warning "Serverclass $serverclass is too long. try less than 28 char." break } Write-Warning "Name set to $serverclass" Write-Warning "Instance Type set to $instancetype" if($url -and $hostedzonename){ Write-Warning "DnsNameset to $url to be added to $hostedzonename zone." } # ask user to confirm config if running from a non aws host if($null -eq $getregion){ $continue = Read-Host "Continue? [y/n]" if($continue -ne 'y'){ break } } # AMI $ami_data = Get-EC2Image -owner $amiownerid -Region $region | Where-Object {$_.Name -like "$amifilter"} | Sort-Object CreationDate | Select-Object -Last 1 if($null -eq $ami_data){ $ami_data = Get-EC2ImageByName -Name $amifilter -Region $region # use the latest ami for select os } Write-Warning "AMI set to $($ami_data.Name)" Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) Locked ami $($ami_data.ImageId) $($ami_data.Name)." | out-file -append -encoding ascii $logfile # IAM $iamrole = "iam-role-" + $serverclass # required by launch config $iam_doco = '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' $iamprofilerolename = "iam-profile-" + $serverclass $iampolicyname = "iam-policy-" + $serverclass New-IAMRole -RoleName $iamrole -Description $serverclass -AssumeRolePolicyDocument $iam_doco -Region $region $iaminstanceprofile = New-IAMInstanceProfile -InstanceProfileName $iamprofilerolename -Force -Region $region Register-IAMRolePolicy -RoleName $iamrole -PolicyArn arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM -PassThru -Force -Region $region Add-IAMRoleToInstanceProfile -RoleName $iamrole -InstanceProfileName $iamprofilerolename -PassThru -Force -Region $region $iampolicy = Get-IAMAttachedRolePolicies -RoleName $iamrole -Region $region | Where-Object {$_.PolicyName -eq $iampolicyname} if($null -eq $iampolicy){ # create Write-IAMRolePolicy -RoleName $iamrole -PolicyName $iampolicyname -PolicyDocument $policyjson -PassThru # New-IAMPolicy -PolicyName $iampolicyname -Description "Created by powershell" -PolicyDocument $policyjson -Region $region } else{ # update New-IAMPolicyVersion -PolicyArn $iampolicy.PolicyArn -PolicyDocument $policyjson -Region $region } Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) Created IAM role w/ policy." | out-file -append -encoding ascii $logfile # ^ based on http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_manage_modify.html#roles-managingrole-editing-cli # AZs # $availability_zones = Get-EC2AvailabilityZone -Region $region # not required (commented out) # SECURITY GROUP $sec_group_name_elb = "sec-group-elb-" + $serverclass $sec_group_name_ec2 = "sec-group-ec2-" + $serverclass # configure as many ingress rules u need using $ip1,$ip2,etc add them in Grant-EC2SecurityGroupIngress @() $ip1_elb = new-object Amazon.EC2.Model.IpPermission $ip1_elb.IpProtocol = "tcp" $ip1_elb.FromPort = $worldport $ip1_elb.ToPort = $instanceport $ip1_elb.IpRanges.Add("0.0.0.0/0") $ip1_ec2 = new-object Amazon.EC2.Model.IpPermission $ip1_ec2.IpProtocol = "tcp" $ip1_ec2.FromPort = $instanceport $ip1_ec2.ToPort = $instanceport $ip1_ec2.IpRanges.Add("0.0.0.0/0") $sec_group_elb = Get-EC2SecurityGroup -Region $region | Where-Object {$_.GroupName -eq $sec_group_name_elb} $sec_group_ec2 = Get-EC2SecurityGroup -Region $region | Where-Object {$_.GroupName -eq $sec_group_name_ec2} if(!($sec_group_elb)){ New-EC2SecurityGroup -Description $serverclass -GroupName $sec_group_name_elb -VpcId $vpc.VpcId -Region $region # get the sec group again in case it was created $sec_group_elb = Get-EC2SecurityGroup -Region $region | Where-Object {$_.GroupName -eq $sec_group_name_elb} Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) $sec_group_name_elb created." | out-file -append -encoding ascii $logfile } if(!($sec_group_ec2)){ New-EC2SecurityGroup -Description $serverclass -GroupName $sec_group_name_ec2 -VpcId $vpc.VpcId -Region $region # get the sec group again in case it was created $sec_group_ec2 = Get-EC2SecurityGroup -Region $region | Where-Object {$_.GroupName -eq $sec_group_name_ec2} Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) $sec_group_name_ec2 created." | out-file -append -encoding ascii $logfile } if($null -ne $sec_group_elb){ Grant-EC2SecurityGroupIngress -GroupId $sec_group_elb.GroupId -IpPermission @( $ip1_elb ) -Region $region Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) $($sec_group_elb.GroupName) permissions granted." | out-file -append -encoding ascii $logfile } if($null -ne $sec_group_ec2){ Grant-EC2SecurityGroupIngress -GroupId $sec_group_ec2.GroupId -IpPermission @( $ip1_ec2 ) -Region $region Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) $($sec_group_ec2.GroupName) permissions granted." | out-file -append -encoding ascii $logfile } Start-Sleep -s 5 # SUBNET if($subnetsids){ $subnetid = @() foreach($sub in $subnetsids){ $subnet = Get-EC2Subnet -Region $region | Where-Object {$_.VpcId -like $vpc.VpcId -and $_.SubnetId -like $sub} $subnid = $subnet.SubnetId $subnetid += $subnid.tostring() } $subnetid } else{ $subnet = Get-EC2Subnet -Region $region | Where-Object {$_.VpcId -like $vpc.VpcId} $subnetid = $subnet.SubnetId $subnetid } # ELB $elb_name = "elb-" + $serverclass $httpListener = New-Object Amazon.ElasticLoadBalancing.Model.Listener $httpListener.Protocol = "http" $httpListener.LoadBalancerPort = $worldport $httpListener.InstanceProtocol = "http" $httpListener.InstancePort = $instanceport try{ New-ELBLoadBalancer -LoadBalancerName $elb_name -SecurityGroup $sec_group_elb.GroupId -Subnet @( $subnetid ) -Listener $httpListener -Region $region } catch{ New-ELBLoadBalancer -LoadBalancerName $elb_name -SecurityGroup $sec_group_elb.GroupId -Subnet ($subnetid | Select-Object -Last 1) -Listener $httpListener -Region $region } $elb_data = Get-ELBLoadBalancer -LoadBalancerName $elb_name -Region $region Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) $elb_name created with AZ $($subnet.AvailabilityZone)." | out-file -append -encoding ascii $logfile Start-Sleep -s 10 # LAUNCH CONFIGURATION $lconfig_name = "lconfig-" + $serverclass $lconfig_instance_type = $instancetype # userdata required base64 encoding $Bytes = [System.Text.Encoding]::ascii.GetBytes($userdata) $EncodedUserdata =[Convert]::ToBase64String($Bytes) <# $DecodeUserData = [System.Convert]::FromBase64String($EncodedUserdata) [System.Text.Encoding]::ascii.GetString($DecodeUserData) $DecodeUserData #> New-ASLaunchConfiguration -LaunchConfigurationName $lconfig_name -ImageId $ami_data.ImageId -UserData $EncodedUserdata -SecurityGroup $sec_group_elb.GroupId -InstanceType $lconfig_instance_type -InstanceMonitoring_Enabled $true -IamInstanceProfile $iaminstanceprofile.Arn -Force -Region $region Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) $lconfig_name created. With instance profile $($iaminstanceprofile.Arn)" | out-file -append -encoding ascii $logfile # ASG $asg_tag0 = New-Object Amazon.AutoScaling.Model.Tag $asg_tag0.Key = "Name" $asg_tag0.Value = "_" + "$serverclass" $asg_tag1 = New-Object Amazon.AutoScaling.Model.Tag $asg_tag1.Key = "serverclass" $asg_tag1.Value = "$serverclass" $asg_tag2 = New-Object Amazon.AutoScaling.Model.Tag $asg_tag2.Key = "created-by" $asg_tag2.Value = "powershell.$env:USERNAME.$env:COMPUTERNAME" $asg_tag3 = New-Object Amazon.AutoScaling.Model.Tag $asg_tag3.Key = $tagkey $asg_tag3.Value =$tagvalue $asg_tags = ($asg_tag0,$asg_tag1,$asg_tag2,$asg_tag3) $asg_name = "asg-" + $serverclass $asgsubnets = $subnetid -join "," $asgsubnets New-ASAutoScalingGroup -AutoScalingGroupName $asg_name -LoadBalancerName $elb_name -LaunchConfigurationName $lconfig_name -VPCZoneIdentifier $asgsubnets -Tag @($asg_tags) -MinSize 1 -MaxSize 1 -Region $region Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) $asg_name created." | out-file -append -encoding ascii $logfile $asg = Get-ASAutoScalingGroup -AutoScalingGroupName $asg_name -Region $region Start-Sleep -s 45 if($asg){ Update-ASAutoScalingGroup -AutoScalingGroupName $asg_name -MaxSize 1 -MinSize 1 -HealthCheckType EC2 -HealthCheckGracePeriod 30 -Region $region Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) $asg_name updated sizes and healthcheck." | out-file -append -encoding ascii $logfile } else{ Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) ERROR $asg_name not found." | out-file -append -encoding ascii $logfile } # R53 if($url -and $hostedzonename){ $HostedZoneId = Get-R53HostedZones | Where-Object {$_.Name -eq $hostedzonename} $elb_data.DNSName $change = New-Object Amazon.Route53.Model.Change $change.Action = "CREATE" $change.ResourceRecordSet = New-Object Amazon.Route53.Model.ResourceRecordSet $change.ResourceRecordSet.Name = $url $change.ResourceRecordSet.Type = "A" $change.ResourceRecordSet.AliasTarget = New-Object Amazon.Route53.Model.AliasTarget $change.ResourceRecordSet.AliasTarget.HostedZoneId = $HostedZoneId.Id $change.ResourceRecordSet.AliasTarget.DNSName = $elb_data.DNSName $change.ResourceRecordSet.AliasTarget.EvaluateTargetHealth = $false $params = @{ HostedZoneId=$($HostedZoneId.Id) ChangeBatch_Comment="This change batch creates an alias resource record set, for $serverclass, pointing to $($elb_data.DNSName)" ChangeBatch_Change=$change } Edit-R53ResourceRecordSet @params Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) $url added to $hostedzonename with endpoint $($elb_data.DNSName)." | out-file -append -encoding ascii $logfile } Write-Output "$(Get-Date -Format dd/MMM/yyyy:HH:mm:ss) ------------------- Finished." | out-file -append -encoding ascii $logfile } # close process } # close function |