© Brian Beach, Steven Armentrout, Rodney Bozo, Emmanuel Tsouris 2019
B. Beach et al.Pro PowerShell for Amazon Web Serviceshttps://doi.org/10.1007/978-1-4842-4850-8_9

9. Identity and Access Management

Brian Beach1 , Steven Armentrout2, Rodney Bozo3 and Emmanuel Tsouris4
(1)
Raleigh, NC, USA
(2)
Mountlake Terrace, WA, USA
(3)
Sterling, VA, USA
(4)
North Bend, WA, USA
 

If you have been following along from the beginning, we have completed all of the examples in this book while signed in as a user with administrator privileges. While this is a convenient way to learn a new technology, you should never run a production system with administrator privileges. If part of the system were compromised, you want to ensure you limit access as much as possible.

This chapter is all about Identity and Access Management (IAM). IAM is how you manage users, groups, and permissions. In this chapter, I show you how to create users and groups. I also explain how IAM policies work and how to create them. IAM policies describe which resources a user can access and the operations they can perform on those resources. You will see that IAM gives you unprecedented control over access.

Finally, in the two exercises at the end of the chapter, we will create a framework for least privileged access and grant access to billing and support. Let’s get started.

Managing Users

Let’s begin by adding a few users to our AWS account. We added a single user back in Chapter 2 using the AWS Management Console. Now let’s add a few using PowerShell.

To add users, you use the New-IAMUser command . The following script will add six users:
New-IAMUser -UserName 'alice'
New-IAMUser -UserName 'bob'
New-IAMUser -UserName 'chris'
New-IAMUser -UserName 'dan'
New-IAMUser -UserName 'eve'
New-IAMUser -UserName 'frank'
As you might expect, there is also a Get-IAMUser command that can be used to get information about a user, such as the username and date the account was created.
Get-IAMUser -UserName 'alice'
Get-IAMUser works a bit differently from other commands. Most “get” methods return a list of all objects when you call them without parameters. If you call Get-IAMUser without the UserName parameter, it returns the currently logged in user. This is useful when writing a generic script that needs to discover who is currently logged in. For example, you might want to tag an instance with the name of the current user.
$User = Get-IAMUser
$Tag = New-Object Amazon.EC2.Model.Tag
$Tag.Key ='Owner'
$Tag.Value = $User.UserName

Note that Get-IAMUser only works for users. Toward the end of this chapter, we will talk about IAM roles. If you are writing code to get the current user, I suggest using Get-STSCallerIdentity. Get-STSCallerIdentity will work for both users and roles.

If you want to list all of the users in the account, use the Get-IAMUsers command.
Get-IAMUsers | Format-Table
You may remember from Chapter 2 that there are multiple types of credentials. We discussed that users need a password to access the AWS Management Console and access keys to use the REST API and PowerShell. But not all users require both types of credentials. To allow a user to access the AWS Management Console, you must assign a password using the New-IAMLoginProfile command.
New-IAMLoginProfile -UserName 'alice' -Password 'PASSWORD' -PasswordResetRequired:$True
Note that PasswordResetRequired is optional. If you omit PasswordResetRequired, the user will not be forced to reset their password on the next console login. There are two commands for changing a user’s password. The first, Edit-IAMPassword, allows a user to change their own password. It always acts on the current user and requires the old password.
Edit-IAMPassword -OldPassword 'PASSWORD' -NewPassword 'Du2[/uiCq8LKjW'
The second, Update-IAMLoginProfile , is an administrative action. Update-IAMLoginProfile can change any user’s password and does not require the original password.
Update-IAMLoginProfile -UserName alice -Password 'Du2[/uiCq8LKjW'
If you want to remove a login profile, and deny access to the AWS Management Console, use the Remove-IAMLoginProfile command .
Remove-IAMLoginProfile -UserName 'alice' -Force
If you want the user to be able to use the REST API, you must create an access key using the New-IAMAccessKey command. Remember that we are using the REST API with PowerShell. Therefore, a user needs an access key to use PowerShell for AWS. The New-IAMAccessKey command returns an object that includes both the AccessKeyId and SecretAccessKey .
$Keys = New-IAMAccessKey -UserName 'alice'
$Keys.AccessKeyId
$Keys.SecretAccessKey
Remember to save the secret key because you cannot get it again. To store a copy in your PowerShell session, you can use the Set-AWSCredentials command discussed in Chapter 2. For example:
$Keys = New-IAMAccessKey -UserName 'alice'
Set-AWSCredentials -AccessKey $Keys.AccessKeyId -SecretKey $Keys.SecretAccessKey -StoreAs 'alice'
If you want to delete a user’s access keys, you can use Remove-IAMAccessKey . A user can have more than one Access Key; therefore, you must specify which Access Key to remove.
Remove-IAMAccessKey -UserName 'alice' -AccessKeyId 'AKIAJV64XS4XLRAJIBAQ' -Force
You may find that you need to check if a user has either a password or access keys. You can use Get-IAMLoginProfile and Get-IAMAccessKey to check if they exist.
Get-IAMLoginProfile -UserName 'alice'
Get-IAMAccessKey -UserName 'alice'
Both of these methods return a create date for the credential. Given that each user can have two sets of access keys, the security conscious user will rotate these keys on a regular basis. For example, you might replace the older set of keys every 30 days. The following script will find the oldest set of keys for a user, delete them, and create a new set.
$Key = Get-IAMAccessKey -UserName 'alice' | Sort-Object CreateDate -Descending | Select AccessKeyId -First 1
Remove-IAMAccessKey -UserName 'alice' -AccessKeyId $Key.AccessKeyId-Force
$Keys = New-IAMAccessKey -UserName 'alice'
$Keys.AccessKeyId
$Keys.SecretAccessKey

Now that we have a user created, we need to assign the user permissions. Before we do, let’s look at groups. Groups allow you to group related users together and assign them permissions as a unit. This process is usually less time-consuming and less error prone.

Managing Groups

When you apply permissions to individual users, it is very difficult to keep track of who has access to which resources. Grouping related users makes managing permissions much easier. Groups reduce the number of unique permissions sets you need to keep track of. (In the first exercise at the end of this chapter, we build a set of common groups as a starting point.)

To create a new group, use the New-IAMGroup command and assign a name.
New-IAMGroup -GroupName 'AWS_USERS'
Initially the group is empty. To add a user to a group, use the Add-IAMUserToGroup command and pass the name of the user and the group to add him or her to.
Add-IAMUserToGroup -UserName 'alice' -GroupName 'AWS_USERS'
If you want to remove a user from a group, use the Remove-IAMUserFromGroup command passing the name of the user and the group to remove him or her from.
Remove-IAMUserFromGroup -UserName 'alice' -GroupName 'AWS_USERS' -Force
Listing groups is similar to listing users. You use the Get-IAMGroups (plural) command to list all the groups in your account.
Get-IAMGroups
You use the Get-IAMGroup (singular) command to get a specific group.
Get-IAMGroup -GroupName 'AWS_USERS'

Note that these two commands return different information. The Get-IAMGroups (plural) command returns a group object that does not include the group members. The Get-IAMGroup (singular) command returns a GetGroupResult object that includes the group and a collection of users.

Therefore, to list the members of a group, use Get-IAMGroup and then read the users property.
(Get-IAMGroup -GroupName 'AWS_USERS').Users
To get the opposite – a list of groups a user is a member of – you can use the Get-IAMGroupForUser command . For example:
Get-IAMGroupForUser 'alice'
Unlike the Get-IAMUser command , Get-IAMGroupForUser cannot be called without a group parameter. It would be nice if calling Get-IAMGroupForUser would list the groups the current user is a member of. We can use a little PowerShell magic to combine Get-IAMUser and Get-IAMGroupForUser to get the list. For example:
Get-IAMUser | Get-IAMGroupForUser

At this point we have created a few users and groups and have added users to groups. But, our users still don’t have permission to do anything. In the next section, we will grant permission to our users.

Managing Policies

We use policies to grant permissions to users and groups. Policies are JSON statements that describe what API calls a user or group is allowed to call. You can grant or deny access to every API call. Before we get started, let’s do a quick review of JSON.

JSON Primer

JavaScript Object Notation (JSON) was first used to send objects from a web server to a browser. JSON uses key/value pairs to represent attributes. Here are a few examples of attributes in JSON:
"Name": "Joe"
"Age": 35
"Male": true
An array can be represented by a single key and multiple values in square brackets. For example:
"Children": ["Mary", "Charles", "Sam"]
An object is simply a list of key/value pairs separated by commas and enclosed in curly braces. For example, we might represent a person as
{
     "Name": "Joe",
     "Age": 35,
     "Male": true,
     "Children": ["Mary", "Charles", "Sam"]
}
We can also nest objects inside other objects. For example:
{
     "Name": "Joe",
     "Age": 35,
     "Male": true,
     "Children": [
          {
               "Name": "Mary",
               "Age": 3,
               "Male": false
          },
          {
               "Name": "Charles",
               "Age": 5,
               "Male": true
          },
          {
               "Name": "Sam",
               "Age": 7,
               "Male": true
          }
     ]
}

This is a very brief introduction, but you can see that JSON can be used to represent very complex structures. I could write a whole book on JSON and others have but this is all we need to understand IAM policy statements.

Policy statements are written in JSON. The statement must include three sections: effect, action, and resource. The effect of the statement is to either allow access or deny access. The action is a list of API calls that are allowed. The resource section lists the objects the user is allowed to act on. For example, the following statement will allow a user to call any method on any object. In other words, this is an administrator policy.
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}

Policy Actions

Actions determine which API calls are allowed or denied by a policy. Remember that PowerShell commands call API WebMethods. In other words, you can grant or deny access to each individual PowerShell command.

Before we can write a policy, we need to know the API method name. There is a helper cmdlet, Get-AWSCmdletName, which you can use to map cmdlets to API methods. For example:
PS C:>  Get-AWSCmdletName | Where CmdletName -eq New-Ec2Instance
CmdletName      ServiceOperation ServiceName
----------      ---------------- -----------
New-EC2Instance RunInstances     Amazon Elastic Compute Cloud
Now that we know the API names, let’s write a custom policy. We use an array to list multiple methods in a single policy. Note that the method name is preceded by the service type (i.e., "iam:") The following example allows access to all the read methods in IAM. In other words, this policy grants read-only access to IAM.
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
         "iam:GetAccountPasswordPolicy",
         "iam:GetAccountSummary",
         "iam:GetGroup",
         "iam:GetGroupPolicy",
         "iam:GetInstanceProfile",
         "iam:GetLoginProfile",
         "iam:GetRole",
         "iam:GetRolePolicy",
         "iam:GetServerCertificate",
         "iam:GetUser",
         "iam:GetUserPolicy",
         "iam:ListAccessKeys",
         "iam:ListAccountAliases",
         "iam:ListGroupPolicies",
         "iam:ListGroups",
         "iam:ListGroupsForUser",
         "iam:ListInstanceProfiles",
         "iam:ListInstanceProfilesForRole",
         "iam:ListMFADevices",
         "iam:ListRolePolicies",
         "iam:ListRoles",
         "iam:ListServerCertificates",
         "iam:ListSigningCertificates",
         "iam:ListUserPolicies",
         "iam:ListUsers",
         "iam:ListVirtualMFADevices"
       ],
      "Resource": "*"
    }
  ]
}
You can also use a wildcard or “∗” character to specify API methods that start with a specific pattern. For example, we could simplify the preceding policy using wildcards.
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
         "iam:Get*",
         "iam:List*"
         ],
      "Resource": "*"
    }
  ]
}
Often you want to grant access to an entire service such as EC2. We can also scope a wildcard to grant access to a specific service. The following example will grant access to EC2 and S3.
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
         "ec2:*",
         "s3:*"
         ],
      "Resource": "*"
    }
  ]
}

As you can see, IAM policies allow fine-grained control over access. In Exercise 9.1 we will develop a set of least privileged roles for EC2. Now let’s look at resources.

Policy Resources

So far, the policies we have written apply to all resources. When we granted access to S3 in the following example, we allowed the user to act on all objects in all buckets. Some services allow you to scope the access. In S3, we might want to allow access to a specific bucket or folder.

For example, to scope access to the “MyBucket” bucket
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": *,
      "Resource": "arn:aws:s3:::MyBucket"
    }
  ]
}
The resource statement is always written using an Amazon Resource Name (ARN). An ARN is used to uniquely identify an AWS resource across accounts and regions. The ARN format is as follows:
arn:aws:service:region:account:resource
Note that S3 is a special case. The bucket name is already unique; therefore, the ARN does not include the account and region and follows the format
arn:aws:s3:::BUCKET/KEY
Many, but not all, services support resource-level permission that allows you to scope a policy to specific resources. For example, you could scope access to a specific object in S3 as follows:
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": *,
      "Resource": "arn:aws:s3:::MyBucket/MyFolder/MyFile.txt"
    }
  ]
}
Of course, you can use wildcards here as well. The following example will scope access to all objects in the MyFolder folder in the MyBucket bucket:
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": *,
      "Resource": "arn:aws:s3:::MyBucket/MyFolder/*"
    }
  ]
}
IAM also allows a few variables in the policy statements. (See the sidebar for a list of supported variables.) Variables make it easier to create a generic policy . For example, let’s assume that every user has a personal folder in S3 that is named with the user’s username. It would be really tedious to write a policy for each user in the following format:
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": *,
      "Resource": "arn:aws:s3:::MyBucket/alice/*"
    }
  ]
}
You can write a generic policy that grants each user access to his or her own folder as follows:
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": *,
      "Resource": "arn:aws:s3:::MyBucket/${aws:username}/*"
    }
  ]
}

Policy Variables

Here is a list of variables supported in IAM policy statements.

Name

Description

aws:CurrentTime

Date and time of the request

aws:EpochTime

Date and time in Unix time format

aws:TokenIssueTime

Date and time that temporary credentials were issued

aws:principaltype

A value that indicates whether the principal is an account, user, federated, or assumed role (see the explanation that follows)

aws:SecureTransport

Boolean representing whether the request was sent using SSL

aws:SourceIp

The requester’s IP address, for use with IP address conditions

aws:UserAgent

Information about the requester’s client application, for use with string conditions

aws:userid

Unique ID for the current user

aws:username

Username of the current user

s3:prefix

Prefix passed in some S3 commands

s3:max-keys

Max-Keys information passed in some S3 commands

sns:Endpoint

Endpoint passed in some SNS calls

sns:Protocol

Protocol passed in some SNS calls

Unfortunately, not all services support resources. For example, S3 and IAM do, but EC2 does not. Luckily we can use conditions to control access to EC2 objects by tag. But, before we talk about conditions, let’s look at policy actions.

Policy Actions

All of the policy statements we have written so far allow access to a resource. You can also deny access to a resource by using the deny action. For example, I could keep a user from terminating instances by denying access to the ec2:TerminateInstances action .
{
  "Statement": [
    {
      "Effect": "Deny",
      "Action": "ec2:TerminateInstances",
      "Resource": "*"
    }
  ]
}

Effect, resource, and action are required components of every policy statement. There are also numerous optional components. I’m not going to cover all of the options here, but I do want to discuss conditions. Conditions are very useful for controlling access to EC2. Let’s have a look.

Policy Conditions

Conditions allow you to write custom logic to determine if an action is allowed. This is a complex topic that could easily fill a chapter. I am only going to show you how to write conditions based on EC2 tags. You can read more about conditions in the IAM user guide.

Imagine you want to allow users to terminate instances tagged DEV but not those considered QA or PROD. You could grant access to the terminate action, but use a condition to limit access to those instances that have a tag called "environment" with the value "dev".
{
   "Version": "2012-10-17",
   "Statement": [{
      "Effect": "Allow",
      "Action": "ec2:TerminateInstances",
      "Resource": "arn:aws:ec2:us-east-1:123456789012:instance/*",
      "Condition": {
        "StringEquals": {
          "ec2:ResourceTag/environment": "dev"
        }
      }
    }
   ]
}

Notice that I have included the optional version to tell AWS this policy requires the latest version of the policy language. Also notice the format of the resource ARN. Remember to replace the 123456789012 with your account number.

Now that we know how to write a policy, let’s associate it with a user and group using PowerShell.

Creating Policies with PowerShell

Creating an IAM policy in PowerShell is really easy. You simply create the JSON statement as a string and then associate it with a user or group. For example, to grant Alice full control, use the Write-IAMUserPolicy command.
$Policy = @"
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}
"@
Write-IAMUserPolicy -UserName "alice" -PolicyName "alice-FullControl" -PolicyDocument $Policy
Assigning a policy to a group is just as easy. For example, to grant full control to the ADMINS group, use the Write-IAMGroupPolicy command.
Write-IAMGroupPolicy -GroupName "ADMINS" -PolicyName "ADMINS-FullControl" -PolicyDocument $Policy

As you can see, IAM policies give you fine-grained control over access to AWS. You can be very specific about who has access to which resources. The details are all contained in the policy statement. In Exercise 9.1 we will create a common set of groups with least privileged policy defined. Before we end the section on policies, let’s spend a moment on managed policies.

Managed Policies

Up to this point, we have been creating policies by hand. In practice, this can get tedious. In addition, it is easy to make mistakes that grant more permission than you intend and compromise security. As you might expect, there are many common patterns that emerge. For example, most organizations have a team of system administrators and they grant them access to EC2, ECS, and so on, or network administrators that need access to VPC, Route 53, and so on.

Luckily, AWS has created a collection of managed policies that solve common problems. It is often much easier to use these policies rather than writing your own policies. In addition, you can create your own managed policies.

Why would you want to create a managed policy when you can attach a policy directly to a user? You can define a policy once and use it across many users, groups, and roles. This promotes consistent permissions across your security principals. In addition, managed policies support versioning so you can maintain a history of changes. Policies attached directly to a user do not support versioning.

Let’s begin by looking at the managed policies in your account.
 Get-IAMPolicies
If you want to list only those managed policies defined by AWS, excluding any customer managed policies, you can add the Scope=AWS attribute.
Get-IAMPolicies -Scope AWS
Conversely, you can list only those customer managed policies by adding the scope equals local attribute.
Get-IAMPolicies -Scope local
You can get details about a specific policy using the Get-IAMPolicy cmdlet. This command requires the Arn of the policy you are interested in.
Get-IAMPolicy -PolicyArn arn:aws:iam::aws:policy/AmazonGlacierReadOnlyAccess
You may notice that this command does not return the actual policy. Why? Remember that managed policies support versions. Therefore, each managed policy may have many versions. You can list the policies with the Get-IAMPolicyVersions cmdlet.
Get-IAMPolicyVersions -PolicyArn arn:aws:iam::aws:policy/AmazonGlacierReadOnlyAccess
And, you can get a specific version using the Get-IAMPolicyVersion cmdlet.
Get-IAMPolicyVersion -PolicyArn arn:aws:iam::aws:policy/AmazonGlacierReadOnlyAccess -VersionId v1
As I mentioned earlier, you can create your own managed policies. This allows you define your policies once and maintain version history. For example, let’s assume you want to create a managed policy for system administrators. Of course, there is already an AWS-defined policy for system administrators. In general, I recommend using the built-in policies rather than writing your own. However, let’s assume we want to create our own. Here I am granting the system administrators full control over EC2. Note that the version attribute is required when creating a managed policy.
$Policy = @"
{
   "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "ec2:*",
      "Resource": "*"
    }
  ]
}
"@
New-IAMPolicy -PolicyName MySysAdminPolicy -PolicyDocument $Policy
Now, let’s assume that we have started using containers and want to also give you system administrators access to Elastic Container Service (ECS) in addition to EC2. Therefore, I update the policy with a new version. Notice that I am setting the new version as the default.
$Policy = @"
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["ec2:*", "ecs:*"],
      "Resource": "*"
    }
  ]
}
"@
New-IAMPolicyVersion -PolicyArn arn:aws:iam::123456789012:policy/MySysAdminPolicy -PolicyDocument $Policy -SetAsDefault $true
Finally, let’s assume we changed our mind and want to roll back to version 1. We can do this with the Set-IAMDefaultPolicyVersion cmdlet .
Set-IAMDefaultPolicyVersion -PolicyArn arn:aws:iam::123456789012:policy/MySysAdminPolicy -VersionId v1
This command will update the policy of all the security principals (user, groups, and roles) that are using the policy. Of course, the astute reader realizes that there are no principals using the policy. At this point we have created a policy, but we have not assigned the policy to a principal. The last thing we need to do is add the policy to a user, group, or role. Note: We will talk more about roles in the next section. Let’s add the managed policy to the user Alice we created earlier.
Register-IAMUserPolicy -UserName alice -PolicyArn arn:aws:iam::123456789012:policy/MySysAdminPolicy

Managing Roles

Remember from Chapter 2 that an IAM role can be used to associate a policy with an instance, rather than a user. This is just one example of a much more powerful concept. Roles allow you assign permission to AWS services, AWS accounts, SAML identities, and other resources. Let’s look a few examples.

To list the roles defined in your account, use the Get-IAMRoles command . If you run this command, you should see the “AdminRole” we created using the AWS Management Console in Chapter 2.
Get-IAMRoles
You can also get a specific role using the Get-IAMRole command.
Get-IAMRole -Rolename AdminRole

Let’s define a few roles to understand how they work. Creating a new role is similar to the process we used to create a user or group, but we also need a second policy that defines what resources can assume the role. There are two policies required: the first describes who can use the role, and the second describes what the role can do.

Let’s begin by defining who can use this role. The policy shown here allows the EC2 service to assume this role. In other words, this policy can be used by EC2 instances, but not ECS containers.
$AssumeRolePolicy = @"
{
  "Version":"2008-10-17",
  "Statement":[
    {
      "Sid":"",
      "Effect":"Allow",
      "Principal":{"Service":"ec2.amazonaws.com"},
      "Action":"sts:AssumeRole"
    }
  ]
}
"@
Next, we create an access policy just like we did in the prior section. This policy gives the role administrator access to all services. Note that I cannot think of any reason to create an EC2 role with administrator permissions. You should be creating a policy with much less permission than I am in the example.
$AccessPolicy = @"
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}
"@
Now we can create the role using the New-IAMRole command, passing in the access policy.
New-IAMRole -RoleName 'MyAdminRole' -AssumeRolePolicyDocument $AssumeRolePolicy
Next, we use Write-IAMRolePolicy to associate the access policy to the role, just like we did with users and groups earlier.
Write-IAMRolePolicy -RoleName 'MyAdminRole' -PolicyName 'MyAdminRole-FullControl' -PolicyDocument $AccessPolicy
For most roles this is all you need to do. However, roles that will be assigned to EC2 instances need one last step. Before you can associate a role with an EC2 instance, you must create a new instance profile and add the new role to it.
New-IAMInstanceProfile -InstanceProfileName 'MyAdminRoleInstanceProfile'
Add-IAMRoleToInstanceProfile -RoleName 'MyAdminRole' -InstanceProfileName  'MyAdminRoleInstanceProfile'
Let’s look at another example. This time we will create a cross-account role. A cross-account role is simply a role that can be assumed by users in another account. The process is identical to creating a service role. However, rather than specifying a service as the principal, we specify another AWS account.
$AssumeRolePolicy = @"
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::987654321000:root"},
      "Action": "sts:AssumeRole"
    }
  ]
}
"@
Now we can create the new role just like we did in the prior example.
New-IAMRole -RoleName 'MyCrossAccountRole' -AssumeRolePolicyDocument $AssumeRolePolicy
Rather than attaching a policy directly to this role, I will use a managed policy. Once again, I am using the admin policy in the example, but I want to stress that you should use a role with less privilege in real life.
Register-IAMRolePolicy -RoleName MyCrossAccountRole -PolicyArn arn:aws:iam::aws:policy/AdministratorAccess
You can also use roles to grant permissions to other services. For example, Auto Scaling needs to launch and terminated instances as load changes. You need to create a role that grants the Auto Scaling service permission to make these changes in your account. Luckily, AWS offers service-linked roles. Service-linked roles automatically define all of the permissions needed for a given service so you don’t have to create them manually. Let’s create a service-linked role for Auto Scaling.
New-IAMServiceLinkedRole -AWSServiceName autoscaling.amazonaws.com

Note that you have used Auto Scaling at some point in the past, you will likely get an error because this role already exists.

At this point it should be clear that AWS offers a robust permission model that gives you tremendous control over your identities. Of course, this can get confusing so let’s look at options to audit your IAM policies with PowerShell.

Auditing IAM Access

IAM gives you fine-grained control over your users and groups. It also gives you tools to audit user access. Let’s start with a high-level summary and drill down. At the highest level, Get-IAMAccountSummary will return a report of IAM entities and quotas. We can use this to get the total number of users, groups, and roles. We can also get the number of MFA devices in use and check if MFA is enabled on the root account, along with a lot of other useful data.
Get-IAMAccountSummary
Another useful tool is the credential report. This report includes a high-level summary of each IAM credential (user or group). It will tell you when the user last logged in, how long it’s been since they changed their password, when they last rotated their keys, and so on. You create the report in two steps. First, you request the report. This will take a few seconds so I am going to call Start-Sleep. If you have thousands of users, you may need to wait longer. Then I get the report in CSV format and convert to a PowerShell object. Then I can query the data as usual.
# Request the report
Request-IAMCredentialReport
# Wait for report generation
Start-Sleep 30
# Get the report as CSV
$Report = Get-IAMCredentialReport -AsTextArray | ConvertFrom-Csv
# Print a high-level summary
$Report | Format-Table
# Get details about a specific user
$Report | Where user -eq alice
The credential report tells you about a user’s activity. If you want to see a report of the permissions attached to a user, check out the Get-IAMAccountAuthorizationDetails. Get-IAMAccountAuthorizationDetails will tell you what groups, managed policies, inline policies, and so on are associated with a specific user or group. For example, the following example will get all their permissions for Alice.
(Get-IAMAccountAuthorizationDetails).UserDetailList | Where UserName -eq alice
Finally, you may want to test that a user has permission to complete an action without actually doing it. For example, you may want to test that Alice has permission to terminate an EC2 instance without actually terminating anything. You can use the Test-IAMPrincipalPolicy cmdlet to validate a user’s permissions.
Test-IAMPrincipalPolicy -PolicySourceArn arn:aws:iam::123456789012:user/alice -ActionName ec2:TerminateInstance

At this point you know how to manage permissions for user, groups, and roles. Before we close this chapter, I want to discuss a few miscellaneous IAM commands.

Miscellaneous IAM Commands

I want to discuss a few miscellaneous IAM commands that did not warrant their own section. Therefore, I included them all here.

Managing Password Policy

Users that have access to the AWS Management Console need to have a password. Many organizations require a specific password policy. You can control the IAM password policy using the Update-IAMAccountPasswordPolicy command.
Update-IAMAccountPasswordPolicy
     -MinimumPasswordLength 8
     -RequireSymbols $false
     -RequireNumbers $true
     -RequireUppercaseCharacters $true
     -RequireLowercaseCharacters $true
     -AllowUsersToChangePassword $true

You can also get the current policy using Get-IAMAccountPasswordPolicy and remove the policy using Remove-IAMAccountPasswordPolicy.

Setting the Account Alias

Finally, you can get and set the account alias. Remember from Chapter 2 that the account alias is used to create an easy-to-remember sign-in URL.

You can set the account alias using the New-IAMAccountAlias command.
New-IAMAccountAlias -AccountAlias 'brianbeach'

You can also get the current alias using Get-IAMAccountAlias and remove the alias using Remove-IAMAccountAlias.

That brings us to the exercises. As you have seen, IAM gives you fine-grained control over access to AWS resources. You can be very specific about who has access to which resources. In Exercise 9.1 we create a set of common groups that provide least privileged access. In Exercise 9.2 we will learn how to permit access to billing and support.

Exercise 9.1: Creating Least Privileged Groups

Throughout this book we have been using a single account that has administrator access to all services. Obviously this is a bad idea in production. We only want to allow those permissions that each user needs. Let’s create a few common groups as a starting point.

Note: AWS supplies a series of managed policies called AWS Managed Policies for Job Functions . These policies define roles for common job functions. You should consider using them rather than creating your own policies and groups as I am doing in this example.

Let’s assume that our company is using AWS for development. The main users are software developers. We have a team of AWS experts who support the developers. In addition, the developers are supported by the traditional system administrators and network administrators. The system administrators support the operating system, and the network administrators are responsible for routing, load balancers, and network security.

First, all users require a few common permissions. At a minimum they all need the ability to change their own password. Let’s start by creating a group that allows a user to see the password policy change his or her own password. All users should be a member of this group. Note that all of these examples are included with the source code for this chapter.
$Policy = @"
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
         "iam:ChangePassword",
         "iam:GetAccountPasswordPolicy"
         ],
      "Resource": "*"
    }
  ]
}
"@
New-IAMGroup -GroupName "USERS"
Write-IAMGroupPolicy -GroupName "USERS" -PolicyName "USERS-ChangePassword"
     -PolicyDocument $Policy
Second, the AWS administrators require full access. Let’s create a group that has full control of all services. This should be a very small group of people.
$Policy = @"
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}
"@
New-IAMGroup -GroupName "ADMINS"
Write-IAMGroupPolicy -GroupName "ADMINS" -PolicyName "ADMINS-FullControl"
     -PolicyDocument $Policy
Third, the developers are using continuous development. They need to be able to create, start, stop, and terminate instances. Let’s create a group for the developers.
$Policy = @"
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:AttachVolume",
        "ec2:CopySnapshot",
        "ec2:CreateKeyPair",
        "ec2:CreateSnapshot",
        "ec2:CreateTags",
        "ec2:CreateVolume",
        "ec2:DeleteKeyPair",
        "ec2:DeleteSnapshot",
        "ec2:DeleteTags",
        "ec2:DeleteVolume",
        "ec2:DescribeAddresses",
        "ec2:DescribeAvailabilityZones",
        "ec2:DescribeBundleTasks",
        "ec2:DescribeConversionTasks",
        "ec2:DescribeCustomerGateways",
        "ec2:DescribeDhcpOptions",
        "ec2:DescribeExportTasks",
        "ec2:DescribeImageAttribute",
        "ec2:DescribeImages",
        "ec2:DescribeInstanceAttribute",
        "ec2:DescribeInstances",
        "ec2:DescribeInstanceStatus",
        "ec2:DescribeInternetGateways",
        "ec2:DescribeKeyPairs",
        "ec2:DescribeLicenses",
        "ec2:DescribeNetworkAcls",
        "ec2:DescribeNetworkInterfaceAttribute",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DescribePlacementGroups",
        "ec2:DescribeRegions",
        "ec2:DescribeReservedInstances",
        "ec2:DescribeReservedInstancesOfferings",
        "ec2:DescribeRouteTables",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSnapshotAttribute",
        "ec2:DescribeSnapshots",
        "ec2:DescribeSpotDatafeedSubscription",
        "ec2:DescribeSpotInstanceRequests",
        "ec2:DescribeSpotPriceHistory",
        "ec2:DescribeSubnets",
        "ec2:DescribeTags",
        "ec2:DescribeVolumeAttribute",
        "ec2:DescribeVolumes",
        "ec2:DescribeVolumeStatus",
        "ec2:DescribeVpcs",
        "ec2:DescribeVpnConnections",
        "ec2:DescribeVpnGateways",
        "ec2:DetachVolume",
        "ec2:EnableVolumeIO",
        "ec2:GetConsoleOutput",
        "ec2:GetPasswordData",
        "ec2:ImportKeyPair",
        "ec2:ModifyInstanceAttribute",
        "ec2:ModifySnapshotAttribute",
        "ec2:ModifyVolumeAttribute",
        "ec2:MonitorInstances",
        "ec2:RebootInstances",
        "ec2:ReportInstanceStatus",
        "ec2:ResetInstanceAttribute",
        "ec2:ResetSnapshotAttribute",
        "ec2:RunInstances",
        "ec2:StartInstances",
        "ec2:StopInstances",
        "ec2:TerminateInstances",
        "ec2:UnmonitorInstances",
        "elasticloadbalancing:RegisterInstancesWithLoadBalancer"
      ],
      "Resource": "*"
    }
  ]
}
"@
New-IAMGroup -GroupName "DEVELOPERS"
Write-IAMGroupPolicy -GroupName "DEVELOPERS" -PolicyName "DEVELOPERS-ManageInstances"
     -PolicyDocument $Policy
Fourth, the network administrators need full control over the VPC features. They also create and configure load balancers and manage security groups. On the other hand, network administrators do not need to create and destroy instances. Let’s create a group for the network administrators.
$Policy = @"
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "directconnect:*",
        "ec2:AllocateAddress",
        "ec2:AssociateAddress",
        "ec2:AssociateDhcpOptions",
        "ec2:AssociateRouteTable",
        "ec2:AttachInternetGateway",
        "ec2:AttachNetworkInterface",
        "ec2:AttachVpnGateway",
        "ec2:AuthorizeSecurityGroupEgress",
        "ec2:AuthorizeSecurityGroupIngress",
        "ec2:CreateCustomerGateway",
        "ec2:CreateDhcpOptions",
        "ec2:CreateInternetGateway",
        "ec2:CreateNetworkAcl",
        "ec2:CreateNetworkAclEntry",
        "ec2:CreateNetworkInterface",
        "ec2:CreateRoute",
        "ec2:CreateRouteTable",
        "ec2:CreateSecurityGroup",
        "ec2:CreateSubnet",
        "ec2:CreateTags",
        "ec2:CreateVpc",
        "ec2:CreateVpnConnection",
        "ec2:CreateVpnGateway",
        "ec2:DeleteCustomerGateway",
        "ec2:DeleteDhcpOptions",
        "ec2:DeleteInternetGateway",
        "ec2:DeleteNetworkAcl",
        "ec2:DeleteNetworkAclEntry",
        "ec2:DeleteNetworkInterface",
        "ec2:DeleteRoute",
        "ec2:DeleteRouteTable",
        "ec2:DeleteSecurityGroup",
        "ec2:DeleteSubnet",
        "ec2:DeleteTags",
        "ec2:DeleteVpc",
        "ec2:DeleteVpnConnection",
        "ec2:DeleteVpnGateway",
        "ec2:DescribeAddresses",
        "ec2:DescribeAvailabilityZones",
        "ec2:DescribeBundleTasks",
        "ec2:DescribeConversionTasks",
        "ec2:DescribeCustomerGateways",
        "ec2:DescribeDhcpOptions",
        "ec2:DescribeExportTasks",
        "ec2:DescribeImageAttribute",
        "ec2:DescribeImages",
        "ec2:DescribeInstanceAttribute",
        "ec2:DescribeInstances",
        "ec2:DescribeInstanceStatus",
        "ec2:DescribeInternetGateways",
        "ec2:DescribeKeyPairs",
        "ec2:DescribeLicenses",
        "ec2:DescribeNetworkAcls",
        "ec2:DescribeNetworkInterfaceAttribute",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DescribePlacementGroups",
        "ec2:DescribeRegions",
        "ec2:DescribeReservedInstances",
        "ec2:DescribeReservedInstancesOfferings",
        "ec2:DescribeRouteTables",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSnapshotAttribute",
        "ec2:DescribeSnapshots",
        "ec2:DescribeSpotDatafeedSubscription",
        "ec2:DescribeSpotInstanceRequests",
        "ec2:DescribeSpotPriceHistory",
        "ec2:DescribeSubnets",
        "ec2:DescribeTags",
        "ec2:DescribeVolumeAttribute",
        "ec2:DescribeVolumes",
        "ec2:DescribeVolumeStatus",
        "ec2:DescribeVpcs",
        "ec2:DescribeVpnConnections",
        "ec2:DescribeVpnGateways",
        "ec2:DetachInternetGateway",
        "ec2:DetachNetworkInterface",
        "ec2:DetachVpnGateway",
        "ec2:DisassociateAddress",
        "ec2:DisassociateRouteTable",
        "ec2:GetConsoleOutput",
        "ec2:GetPasswordData",
        "ec2:ModifyNetworkInterfaceAttribute",
        "ec2:MonitorInstances",
        "ec2:ReleaseAddress",
        "ec2:ReplaceNetworkAclAssociation",
        "ec2:ReplaceNetworkAclEntry",
        "ec2:ReplaceRoute",
        "ec2:ReplaceRouteTableAssociation",
        "ec2:ResetNetworkInterfaceAttribute",
        "ec2:RevokeSecurityGroupEgress",
        "ec2:RevokeSecurityGroupIngress",
        "ec2:UnmonitorInstances",
        "elasticloadbalancing:ConfigureHealthCheck",
        "elasticloadbalancing:CreateAppCookieStickinessPolicy",
        "elasticloadbalancing:CreateLBCookieStickinessPolicy",
        "elasticloadbalancing:CreateLoadBalancer",
        "elasticloadbalancing:CreateLoadBalancerListeners",
        "elasticloadbalancing:DeleteLoadBalancer",
        "elasticloadbalancing:DeleteLoadBalancerListeners",
        "elasticloadbalancing:DeleteLoadBalancerPolicy",
        "elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
        "elasticloadbalancing:DescribeInstanceHealth",
        "elasticloadbalancing:DescribeLoadBalancers",
        "elasticloadbalancing:DisableAvailabilityZonesForLoadBalancer",
        "elasticloadbalancing:EnableAvailabilityZonesForLoadBalancer",
        "elasticloadbalancing:RegisterInstancesWithLoadBalancer",
        "elasticloadbalancing:SetLoadBalancerListenerSSLCertificate",
        "elasticloadbalancing:SetLoadBalancerPoliciesOfListener"
      ],
      "Resource": "*"
    }
  ]
}
"@
New-IAMGroup -GroupName "NETWORK_ADMINS"
Write-IAMGroupPolicy -GroupName "NETWORK_ADMINS" -PolicyName
     "NETWORK_ADMINS-ManageNetwork" -PolicyDocument $Policy
Fifth, system administrators need full control over the instances. They need all the access a developer has so they can support the developers. In addition they need to be able to create new Amazon Machine Images (AMIs). They do not need access to the networking features that are being supported by the network administrators. Let’s create a group for the system administrators.
$Policy = @"
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:AttachVolume",
        "ec2:CancelConversionTask",
        "ec2:CancelExportTask",
        "ec2:CancelSpotInstanceRequests",
        "ec2:CopySnapshot",
        "ec2:CreateImage",
        "ec2:CreateInstanceExportTask",
        "ec2:CreateKeyPair",
        "ec2:CreatePlacementGroup",
        "ec2:CreateSnapshot",
        "ec2:CreateSpotDatafeedSubscription",
        "ec2:CreateTags",
        "ec2:CreateVolume",
        "ec2:DeleteKeyPair",
        "ec2:DeletePlacementGroup",
        "ec2:DeleteSnapshot",
        "ec2:DeleteSpotDatafeedSubscription",
        "ec2:DeleteTags",
        "ec2:DeleteVolume",
        "ec2:DeregisterImage",
        "ec2:DescribeAddresses",
        "ec2:DescribeAvailabilityZones",
        "ec2:DescribeBundleTasks",
        "ec2:DescribeConversionTasks",
        "ec2:DescribeCustomerGateways",
        "ec2:DescribeDhcpOptions",
        "ec2:DescribeExportTasks",
        "ec2:DescribeImageAttribute",
        "ec2:DescribeImages",
        "ec2:DescribeInstanceAttribute",
        "ec2:DescribeInstances",
        "ec2:DescribeInstanceStatus",
        "ec2:DescribeInternetGateways",
        "ec2:DescribeKeyPairs",
        "ec2:DescribeLicenses",
        "ec2:DescribeNetworkAcls",
        "ec2:DescribeNetworkInterfaceAttribute",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DescribePlacementGroups",
        "ec2:DescribeRegions",
        "ec2:DescribeReservedInstances",
        "ec2:DescribeReservedInstancesOfferings",
        "ec2:DescribeRouteTables",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSnapshotAttribute",
        "ec2:DescribeSnapshots",
        "ec2:DescribeSpotDatafeedSubscription",
        "ec2:DescribeSpotInstanceRequests",
        "ec2:DescribeSpotPriceHistory",
        "ec2:DescribeSubnets",
        "ec2:DescribeTags",
        "ec2:DescribeVolumeAttribute",
        "ec2:DescribeVolumes",
        "ec2:DescribeVolumeStatus",
        "ec2:DescribeVpcs",
        "ec2:DescribeVpnConnections",
        "ec2:DescribeVpnGateways",
        "ec2:DetachVolume",
        "ec2:EnableVolumeIO",
        "ec2:GetConsoleOutput",
        "ec2:GetPasswordData",
        "ec2:ImportInstance",
        "ec2:ImportKeyPair",
        "ec2:ImportVolume",
        "ec2:ModifyImageAttribute",
        "ec2:ModifyInstanceAttribute",
        "ec2:ModifySnapshotAttribute",
        "ec2:ModifyVolumeAttribute",
        "ec2:MonitorInstances",
        "ec2:PurchaseReservedInstancesOffering",
        "ec2:RebootInstances",
        "ec2:RegisterImage",
        "ec2:ReportInstanceStatus",
        "ec2:RequestSpotInstances",
        "ec2:ResetImageAttribute",
        "ec2:ResetInstanceAttribute",
        "ec2:ResetSnapshotAttribute",
        "ec2:RunInstances",
        "ec2:StartInstances",
        "ec2:StopInstances",
        "ec2:TerminateInstances",
        "ec2:UnmonitorInstances"
      ],
      "Resource": "*"
    }
  ]
}
"@
New-IAMGroup -GroupName "SYS_ADMINS"
Write-IAMGroupPolicy -GroupName "SYS_ADMINS" -PolicyName "SYS_ADMINS-ManageImages"
     -PolicyDocument $Policy

In this exercise we created a group for each of the teams that uses AWS at our fictitious company. Obviously you will need to tweak these groups to fit your company’s needs, but I hope this will create a good framework to get you started. In the next exercise, we will grant access to billing and support to IAM users.

Exercise 9.2: Delegating Account Access To IAM Users

Back in Chapter 2, we discussed the difference between AWS account credentials and IAM users. Remember that the AWS account is the e-mail address you used to create your account. You almost never use this account, but there are a few times you need it. Two of these reasons are accessing your bill and getting support.

By default, you must log in using your AWS account credentials to see your bill or access support, but you can also grant access to IAM users. And, as you might expect, you can control exactly which users can access the billing and support features. Note that you have to pay extra for support.

You cannot enable IAM access to billing using PowerShell. You must sign into the AWS Management Console using your account credentials to enable it. The following steps show you how:

  1. 1.

    Sign into the console using the e-mail address and password you used to create your account.

     
  2. 2.

    Click your name on the menu bar at the top right of the screen.

     
  3. 3.

    Click My Account from the drop-down menu.

     
  4. 4.

    Scroll down until you see the section shown in Figure 9-1.

     
  1. 5.

    Select both the Account Activity Page check box and the Usage Reports Page check box. Click the Activate Now button.

     
../images/319650_2_En_9_Chapter/319650_2_En_9_Fig1_HTML.jpg
Figure 9-1

IAM access to the AWS web site

Next we have to create an IAM policy granting access to IAM users. Interestingly, you cannot configure billing and support from the IAM wizard. You must create the policy manually. Luckily we know exactly how to do that. Let’s create two groups: one for billing and one for support.

To create a group for billing, you allow access to ViewBilling and ViewUsage . Billing is the summary information and usage is the raw detail. Just like the last exercise, we will associate this policy with a new group called BILLING.
$Policy = @"
{
 "Statement": [
   {
    "Action": [
       "aws-portal:ViewBilling",
       "aws-portal:ViewUsage"
    ],
    "Effect": "Allow",
    "Resource": "*"
   }
  ]
}
"@
New-IAMGroup -GroupName "BILLING"
Write-IAMGroupPolicy -GroupName "BILLING"
     -PolicyName "BILLING-BillingAndUsage" -PolicyDocument $Policy
To create a group for support, we will create a policy that allows access to support:* and associate it with a new group called SUPPORT.
$Policy = @"
{
 "Statement": [
   {
    "Action": "support:*",
    "Effect": "Allow",
    "Resource": "*"
   }
  ]
}
"@
New-IAMGroup -GroupName "SUPPORT"
Write-IAMGroupPolicy -GroupName "SUPPORT"
     -PolicyName "SUPPORT-FullAccess" -PolicyDocument $Policy

Now, whenever you want to grant a user access to billing or support, you simply add the user to the appropriate group.

Summary

In this chapter, we saw how IAM provides unprecedented control over access. We learned to create users and manage their passwords and access keys. Then, we learned to create groups and manage membership. We also learned to create roles for EC2 instances.

Next we learned to create policies and saw that IAM offers the granularity to enable least privileged access control over all of the AWS services. In the exercises we created a collection of groups for common IT roles and enabled access to billing and support. This is a great start for creating an enterprise access policy.

In the next chapter, we will focus on Simple Storage Service (S3). S3 is a highly resilient data solution for storing files. This is the data store AWS uses to keep snapshots and RDS backups, but you can use it to store anything you want.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset